diff --git a/.gitignore b/.gitignore index b7e4a0186..88ccbb56f 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ bld_dbg/* bld_rel/* bld_dbg_x64/* bld_rel_x64/* +.vscode # Auto generated files. config.log config.status @@ -75,3 +76,6 @@ src/api/ml/z3.mllib *.bak doc/api doc/code +.vs +examples/**/obj +CMakeSettings.json diff --git a/CMakeLists.txt b/CMakeLists.txt index bc5eecdef..188d8dfde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ endif() ################################################################################ set(Z3_VERSION_MAJOR 4) set(Z3_VERSION_MINOR 8) -set(Z3_VERSION_PATCH 4) +set(Z3_VERSION_PATCH 5) set(Z3_VERSION_TWEAK 0) set(Z3_VERSION "${Z3_VERSION_MAJOR}.${Z3_VERSION_MINOR}.${Z3_VERSION_PATCH}.${Z3_VERSION_TWEAK}") set(Z3_FULL_VERSION_STR "${Z3_VERSION}") # Note this might be modified @@ -205,9 +205,6 @@ message(STATUS "PYTHON_EXECUTABLE: ${PYTHON_EXECUTABLE}") include(${CMAKE_SOURCE_DIR}/cmake/target_arch_detect.cmake) detect_target_architecture(TARGET_ARCHITECTURE) message(STATUS "Detected target architecture: ${TARGET_ARCHITECTURE}") -if ("${TARGET_ARCHITECTURE}" STREQUAL "x86_64") - list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_AMD64_") -endif() ################################################################################ @@ -257,6 +254,15 @@ elseif (CYGWIN) elseif (WIN32) message(STATUS "Platform: Windows") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_WINDOWS") +elseif (EMSCRIPTEN) + message(STATUS "Platform: Emscripten") + list(APPEND Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS + "-Os" + "-s ALLOW_MEMORY_GROWTH=1" + "-s ASSERTIONS=0" + "-s DISABLE_EXCEPTION_CATCHING=0" + "-s ERROR_ON_UNDEFINED_SYMBOLS=1" + ) else() message(FATAL_ERROR "Platform \"${CMAKE_SYSTEM_NAME}\" not recognised") endif() @@ -425,6 +431,15 @@ list(APPEND Z3_DEPENDENT_LIBS ${CMAKE_THREAD_LIBS_INIT}) ################################################################################ include(${CMAKE_SOURCE_DIR}/cmake/compiler_warnings.cmake) +################################################################################ +# Save Clang optimization records +################################################################################ +option(SAVE_CLANG_OPTIMIZATION_RECORDS "Enable saving Clang optimization records." OFF) + +if (SAVE_CLANG_OPTIMIZATION_RECORDS) + z3_add_cxx_flag("-fsave-optimization-record" REQUIRED) +endif() + ################################################################################ # If using Ninja, force color output for Clang (and gcc, disabled to check build). ################################################################################ diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 06618a8f2..ba254db4e 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,5 +1,15 @@ RELEASE NOTES +Version 4.8.4 +============= + +- Notes + - fixes bugs + - a substantial update to how the seq theory solver handles regular + expressions. Other performance improvements to the seq solver. + - Managed .NET DLLs include dotnet standard 1.4 on supported platforms. + - Windows Managed DLLs are strong signed in the released binaries. + Version 4.8.3 ============= - New features diff --git a/cmake/modules/DotnetImports.props.in b/cmake/modules/DotnetImports.props.in new file mode 100644 index 000000000..090d46502 --- /dev/null +++ b/cmake/modules/DotnetImports.props.in @@ -0,0 +1,8 @@ + + + ${_DN_OUTPUT_PATH}/ + ${_DN_XPLAT_LIB_DIR}/ + ${_DN_VERSION} + ${_DN_CUSTOM_BUILDPROPS} + + diff --git a/cmake/modules/FindDotnet.cmake b/cmake/modules/FindDotnet.cmake new file mode 100644 index 000000000..98c5f2079 --- /dev/null +++ b/cmake/modules/FindDotnet.cmake @@ -0,0 +1,471 @@ +#.rst +# FindDotnet +# ---------- +# +# Find DotNet executable, and initialize functions for adding dotnet projects. +# +# Results are reported in the following variables:: +# +# DOTNET_FOUND - True if dotnet executable is found +# DOTNET_EXE - Dotnet executable +# DOTNET_VERSION - Dotnet version as reported by dotnet executable +# NUGET_EXE - Nuget executable (WIN32 only) +# NUGET_CACHE_PATH - Nuget package cache path +# +# The following functions are defined to add dotnet/msbuild projects: +# +# ADD_DOTNET -- add a project to be built by dotnet. +# +# ``` +# ADD_DOTNET( [RELEASE|DEBUG] [X86|X64|ANYCPU] [NETCOREAPP] +# [CONFIG configuration] +# [PLATFORM platform] +# [PACKAGE output_nuget_packages... ] +# [VERSION nuget_package_version] +# [DEPENDS depend_nuget_packages... ] +# [OUTPUT_PATH output_path relative to cmake binary output dir] +# [CUSTOM_BUILDPROPS value....] +# [SOURCES additional_file_dependencies... ] +# [ARGUMENTS additional_build_args...] +# [PACK_ARGUMENTS additional_pack_args...]) +# ``` +# +# RUN_DOTNET -- Run a project with `dotnet run`. The `OUTPUT` argument represents artifacts +# produced by running the .NET program, and can be consumed from other build steps. +# +# ``` +# RUN_DOTNET( [RELEASE|DEBUG] [X86|X64|ANYCPU] [NETCOREAPP] +# [ARGUMENTS program_args...] +# [OUTPUT outputs...] +# [CONFIG configuration] +# [PLATFORM platform] +# [DEPENDS depend_nuget_packages... ] +# [OUTPUT_PATH output_path relative to cmake binary output dir] +# [CUSTOM_BUILDPROPS value....] +# [SOURCES additional_file_dependencies... ]) +# ``` +# +# ADD_MSBUILD -- add a project to be built by msbuild. Windows-only. When building in Unix systems, msbuild targets are skipped. +# +# ``` +# ADD_MSBUILD( [RELEASE|DEBUG] [X86|X64|ANYCPU] [NETCOREAPP] +# [CONFIG configuration] +# [PLATFORM platform] +# [PACKAGE output_nuget_packages... ] +# [DEPENDS depend_nuget_packages... ] +# [CUSTOM_BUILDPROPS value....] +# [SOURCES additional_file_dependencies... ] +# [ARGUMENTS additional_build_args...] +# [PACK_ARGUMENTS additional_pack_args...]) +# ``` +# +# SMOKETEST_DOTNET -- add a dotnet smoke test project to the build. The project will be run during a build, +# and if the program fails to build or run, the build fails. Currently only .NET Core App framework is supported. +# Multiple smoke tests will be run one-by-one to avoid global resource conflicts. +# +# SMOKETEST_DOTNET( [RELEASE|DEBUG] [X86|X64|ANYCPU] [NETCOREAPP] +# [ARGUMENTS program_args...] +# [CONFIG configuration] +# [PLATFORM platform] +# [DEPENDS depend_nuget_packages... ] +# [OUTPUT_PATH output_path relative to cmake binary output dir] +# [CUSTOM_BUILDPROPS value....] +# [SOURCES additional_file_dependencies... ]) +# +# For all the above functions, `RELEASE|DEBUG` overrides `CONFIG`, `X86|X64|ANYCPU` overrides PLATFORM. +# For Unix systems, the target framework defaults to `netstandard2.0`, unless `NETCOREAPP` is specified. +# For Windows, the project is built as-is, allowing multi-targeting. +# +# +# DOTNET_REGISTER_LOCAL_REPOSITORY -- register a local NuGet package repository. +# +# ``` +# DOTNET_REGISTER_LOCAL_REPOSITORY(repo_name repo_path) +# ``` +# +# TEST_DOTNET -- add a dotnet test project to ctest. The project will be run with `dotnet test`, +# and trx test reports will be generated in the build directory. For Windows, all target frameworks +# are tested against. For other platforms, only .NET Core App is tested against. +# Test failures will not fail the build. +# Tests are only run with `ctest -C `, not with `cmake --build ...` +# +# ``` +# TEST_DOTNET( +# [ARGUMENTS additional_dotnet_test_args...] +# [OUTPUT_PATH output_path relative to cmake binary output dir]) +# ``` +# +# GEN_DOTNET_PROPS -- Generates a Directory.Build.props file. The created file is populated with MSBuild properties: +# - DOTNET_PACKAGE_VERSION: a version string that can be referenced in the actual project file as $(DOTNET_PACKAGE_VERSION). +# The version string value can be set with PACKAGE_VERSION argument, and defaults to '1.0.0'. +# - XPLAT_LIB_DIR: points to the cmake build root directory. +# - OutputPath: Points to the cmake binary directory (overridden by OUTPUT_PATH, relatively). Therefore, projects built without cmake will consistently output +# to the cmake build directory. +# - Custom properties can be injected with XML_INJECT argument, which injects an arbitrary string into the project XML file. +# +# ``` +# GEN_DOTNET_PROPS( +# [PACKAGE_VERSION version] +# [XML_INJECT xml_injection]) +# ``` +# +# Require 3.5 for batch copy multiple files + +cmake_minimum_required(VERSION 3.5.0) + +IF(DOTNET_FOUND) + RETURN() +ENDIF() + +SET(NUGET_CACHE_PATH "~/.nuget/packages") +FIND_PROGRAM(DOTNET_EXE dotnet) +SET(DOTNET_MODULE_DIR ${CMAKE_CURRENT_LIST_DIR}) + +IF(NOT DOTNET_EXE) + SET(DOTNET_FOUND FALSE) + IF(Dotnet_FIND_REQUIRED) + MESSAGE(SEND_ERROR "Command 'dotnet' is not found.") + ENDIF() + RETURN() +ENDIF() + +EXECUTE_PROCESS( + COMMAND ${DOTNET_EXE} --version + OUTPUT_VARIABLE DOTNET_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +IF(WIN32) + FIND_PROGRAM(NUGET_EXE nuget PATHS ${CMAKE_BINARY_DIR}/tools) + IF(NUGET_EXE) + MESSAGE("-- Found nuget: ${NUGET_EXE}") + ELSE() + SET(NUGET_EXE ${CMAKE_BINARY_DIR}/tools/nuget.exe) + MESSAGE("-- Downloading nuget...") + FILE(DOWNLOAD https://dist.nuget.org/win-x86-commandline/latest/nuget.exe ${NUGET_EXE}) + MESSAGE("nuget.exe downloaded and saved to ${NUGET_EXE}") + ENDIF() +ENDIF() + +FUNCTION(DOTNET_REGISTER_LOCAL_REPOSITORY repo_name repo_path) + MESSAGE("-- Registering NuGet local repository '${repo_name}' at '${repo_path}'.") + GET_FILENAME_COMPONENT(repo_path ${repo_path} ABSOLUTE) + IF(WIN32) + STRING(REPLACE "/" "\\" repo_path ${repo_path}) + EXECUTE_PROCESS(COMMAND ${NUGET_EXE} sources list OUTPUT_QUIET) + EXECUTE_PROCESS(COMMAND ${NUGET_EXE} sources Remove -Name "${repo_name}" OUTPUT_QUIET ERROR_QUIET) + EXECUTE_PROCESS(COMMAND ${NUGET_EXE} sources Add -Name "${repo_name}" -Source "${repo_path}") + ELSE() + GET_FILENAME_COMPONENT(nuget_config ~/.nuget/NuGet/NuGet.Config ABSOLUTE) + EXECUTE_PROCESS(COMMAND ${DOTNET_EXE} nuget locals all --list OUTPUT_QUIET) + EXECUTE_PROCESS(COMMAND sed -i "/${repo_name}/d" "${nuget_config}") + EXECUTE_PROCESS(COMMAND sed -i "s## \\n #g" "${nuget_config}") + ENDIF() +ENDFUNCTION() + +FUNCTION(DOTNET_GET_DEPS _DN_PROJECT arguments) + CMAKE_PARSE_ARGUMENTS( + # prefix + _DN + # options (flags) + "RELEASE;DEBUG;X86;X64;ANYCPU;NETCOREAPP" + # oneValueArgs + "CONFIG;PLATFORM;VERSION;OUTPUT_PATH" + # multiValueArgs + "PACKAGE;DEPENDS;ARGUMENTS;PACK_ARGUMENTS;OUTPUT;SOURCES;CUSTOM_BUILDPROPS" + # the input arguments + ${arguments}) + + GET_FILENAME_COMPONENT(_DN_abs_proj "${_DN_PROJECT}" ABSOLUTE) + GET_FILENAME_COMPONENT(_DN_proj_dir "${_DN_abs_proj}" DIRECTORY) + GET_FILENAME_COMPONENT(_DN_projname "${_DN_PROJECT}" NAME) + STRING(REGEX REPLACE "\\.[^.]*$" "" _DN_projname_noext ${_DN_projname}) + + FILE(GLOB_RECURSE DOTNET_deps + ${_DN_proj_dir}/*.cs + ${_DN_proj_dir}/*.fs + ${_DN_proj_dir}/*.vb + ${_DN_proj_dir}/*.xaml + ${_DN_proj_dir}/*.resx + ${_DN_proj_dir}/*.xml + ${_DN_proj_dir}/*.*proj + ${_DN_proj_dir}/*.cs + ${_DN_proj_dir}/*.config) + LIST(APPEND DOTNET_deps ${_DN_SOURCES}) + SET(_DN_deps "") + FOREACH(dep ${DOTNET_deps}) + IF(NOT dep MATCHES /obj/ AND NOT dep MATCHES /bin/) + LIST(APPEND _DN_deps ${dep}) + ENDIF() + ENDFOREACH() + + + IF(_DN_RELEASE) + SET(_DN_CONFIG Release) + ELSEIF(_DN_DEBUG) + SET(_DN_CONFIG Debug) + ENDIF() + + IF(NOT _DN_CONFIG) + SET(_DN_CONFIG "$<$:Debug>$<$>:Release>") + ENDIF() + + # If platform is not specified, do not pass the Platform property. + # dotnet will pick the default Platform. + + IF(_DN_X86) + SET(_DN_PLATFORM x86) + ELSEIF(_DN_X64) + SET(_DN_PLATFORM x64) + ELSEIF(_DN_ANYCPU) + SET(_DN_PLATFORM "AnyCPU") + ENDIF() + + # If package version is not set, first fallback to DOTNET_PACKAGE_VERSION + # If again not set, defaults to 1.0.0 + IF(NOT _DN_VERSION) + SET(_DN_VERSION ${DOTNET_PACKAGE_VERSION}) + ENDIF() + IF(NOT _DN_VERSION) + SET(_DN_VERSION "1.0.0") + ENDIF() + + # Set the output path to the binary directory. + # Build outputs in separated output directories prevent overwriting. + # Later we then copy the outputs to the destination. + + IF(NOT _DN_OUTPUT_PATH) + SET(_DN_OUTPUT_PATH ${_DN_projname_noext}) + ENDIF() + + GET_FILENAME_COMPONENT(_DN_OUTPUT_PATH ${CMAKE_BINARY_DIR}/${_DN_OUTPUT_PATH} ABSOLUTE) + + # In a cmake build, the XPLAT libraries are always copied over. + # Set the proper directory for .NET projects. + SET(_DN_XPLAT_LIB_DIR ${CMAKE_BINARY_DIR}) + + SET(DOTNET_PACKAGES ${_DN_PACKAGE} PARENT_SCOPE) + SET(DOTNET_CONFIG ${_DN_CONFIG} PARENT_SCOPE) + SET(DOTNET_PLATFORM ${_DN_PLATFORM} PARENT_SCOPE) + SET(DOTNET_DEPENDS ${_DN_DEPENDS} PARENT_SCOPE) + SET(DOTNET_PROJNAME ${_DN_projname_noext} PARENT_SCOPE) + SET(DOTNET_PROJPATH ${_DN_abs_proj} PARENT_SCOPE) + SET(DOTNET_PROJDIR ${_DN_proj_dir} PARENT_SCOPE) + SET(DOTNET_ARGUMENTS ${_DN_ARGUMENTS} PARENT_SCOPE) + SET(DOTNET_RUN_OUTPUT ${_DN_OUTPUT} PARENT_SCOPE) + SET(DOTNET_PACKAGE_VERSION ${_DN_VERSION} PARENT_SCOPE) + SET(DOTNET_OUTPUT_PATH ${_DN_OUTPUT_PATH} PARENT_SCOPE) + SET(DOTNET_deps ${_DN_deps} PARENT_SCOPE) + + IF(_DN_PLATFORM) + SET(_DN_PLATFORM_PROP "/p:Platform=${_DN_PLATFORM}") + ENDIF() + + IF(_DN_NETCOREAPP) + SET(_DN_BUILD_OPTIONS -f netcoreapp2.0) + SET(_DN_PACK_OPTIONS /p:TargetFrameworks=netcoreapp2.0) + ELSEIF(UNIX) + # Unix builds default to netstandard2.0 + SET(_DN_BUILD_OPTIONS -f netstandard2.0) + SET(_DN_PACK_OPTIONS /p:TargetFrameworks=netstandard2.0) + ENDIF() + + SET(_DN_IMPORT_PROP ${CMAKE_CURRENT_BINARY_DIR}/${_DN_projname}.imports.props) + CONFIGURE_FILE(${DOTNET_MODULE_DIR}/DotnetImports.props.in ${_DN_IMPORT_PROP}) + SET(_DN_IMPORT_ARGS "/p:DirectoryBuildPropsPath=${_DN_IMPORT_PROP}") + + SET(DOTNET_IMPORT_PROPERTIES ${_DN_IMPORT_ARGS} PARENT_SCOPE) + SET(DOTNET_BUILD_PROPERTIES ${_DN_PLATFORM_PROP} ${_DN_IMPORT_ARGS} PARENT_SCOPE) + SET(DOTNET_BUILD_OPTIONS ${_DN_BUILD_OPTIONS} PARENT_SCOPE) + SET(DOTNET_PACK_OPTIONS --include-symbols ${_DN_PACK_OPTIONS} ${_DN_PACK_ARGUMENTS} PARENT_SCOPE) + +ENDFUNCTION() + +MACRO(ADD_DOTNET_DEPENDENCY_TARGETS tgt) + FOREACH(pkg_dep ${DOTNET_DEPENDS}) + ADD_DEPENDENCIES(${tgt}_${DOTNET_PROJNAME} PKG_${pkg_dep}) + MESSAGE(" ${DOTNET_PROJNAME} <- ${pkg_dep}") + ENDFOREACH() + + FOREACH(pkg ${DOTNET_PACKAGES}) + STRING(TOLOWER ${pkg} pkg_lowercase) + GET_FILENAME_COMPONENT(cache_path ${NUGET_CACHE_PATH}/${pkg_lowercase} ABSOLUTE) + IF(WIN32) + SET(rm_command powershell -NoLogo -NoProfile -NonInteractive -Command "Remove-Item -Recurse -Force -ErrorAction Ignore '${cache_path}'\; exit 0") + ELSE() + SET(rm_command rm -rf ${cache_path}) + ENDIF() + ADD_CUSTOM_TARGET( + DOTNET_PURGE_${pkg} + COMMAND ${CMAKE_COMMAND} -E echo "======= [x] Purging nuget package cache for ${pkg}" + COMMAND ${rm_command} + DEPENDS ${DOTNET_deps} + ) + ADD_DEPENDENCIES(${tgt}_${DOTNET_PROJNAME} DOTNET_PURGE_${pkg}) + # Add a target for the built package -- this can be referenced in + # another project. + ADD_CUSTOM_TARGET(PKG_${pkg}) + ADD_DEPENDENCIES(PKG_${pkg} ${tgt}_${DOTNET_PROJNAME}) + MESSAGE("==== ${DOTNET_PROJNAME} -> ${pkg}") + ENDFOREACH() +ENDMACRO() + +MACRO(DOTNET_BUILD_COMMANDS) + IF(${DOTNET_IS_MSBUILD}) + SET(build_dotnet_cmds + COMMAND ${CMAKE_COMMAND} -E echo "======= Building msbuild project ${DOTNET_PROJNAME} [${DOTNET_CONFIG} ${DOTNET_PLATFORM}]" + COMMAND ${NUGET_EXE} restore -Force ${DOTNET_PROJPATH} + COMMAND ${DOTNET_EXE} msbuild ${DOTNET_PROJPATH} /t:Clean ${DOTNET_BUILD_PROPERTIES} /p:Configuration="${DOTNET_CONFIG}" + COMMAND ${DOTNET_EXE} msbuild ${DOTNET_PROJPATH} /t:Build ${DOTNET_BUILD_PROPERTIES} /p:Configuration="${DOTNET_CONFIG}" ${DOTNET_ARGUMENTS}) + SET(build_dotnet_type "msbuild") + ELSE() + SET(build_dotnet_cmds + COMMAND ${CMAKE_COMMAND} -E echo "======= Building .NET project ${DOTNET_PROJNAME} [${DOTNET_CONFIG} ${DOTNET_PLATFORM}]" + COMMAND ${DOTNET_EXE} restore ${DOTNET_PROJPATH} ${DOTNET_IMPORT_PROPERTIES} + COMMAND ${DOTNET_EXE} clean ${DOTNET_PROJPATH} ${DOTNET_BUILD_PROPERTIES} + COMMAND ${DOTNET_EXE} build --no-restore ${DOTNET_PROJPATH} -c ${DOTNET_CONFIG} ${DOTNET_BUILD_PROPERTIES} ${DOTNET_BUILD_OPTIONS} ${DOTNET_ARGUMENTS}) + SET(build_dotnet_type "dotnet") + ENDIF() + + # DOTNET_OUTPUTS refer to artifacts produced, that the BUILD_proj_name target depends on. + SET(DOTNET_OUTPUTS ${CMAKE_CURRENT_BINARY_DIR}/${DOTNET_PROJNAME}.buildtimestamp) + LIST(APPEND build_dotnet_cmds COMMAND ${CMAKE_COMMAND} -E touch ${DOTNET_OUTPUTS}) + IF(NOT "${DOTNET_PACKAGES}" STREQUAL "") + MESSAGE("-- Adding ${build_dotnet_type} project ${DOTNET_PROJPATH} (version ${DOTNET_PACKAGE_VERSION})") + FOREACH(pkg ${DOTNET_PACKAGES}) + LIST(APPEND DOTNET_OUTPUTS ${DOTNET_OUTPUT_PATH}/${pkg}.${DOTNET_PACKAGE_VERSION}.nupkg) + LIST(APPEND DOTNET_OUTPUTS ${DOTNET_OUTPUT_PATH}/${pkg}.${DOTNET_PACKAGE_VERSION}.symbols.nupkg) + LIST(APPEND build_dotnet_cmds COMMAND ${CMAKE_COMMAND} -E remove ${DOTNET_OUTPUT_PATH}/${pkg}.${DOTNET_PACKAGE_VERSION}.nupkg) + LIST(APPEND build_dotnet_cmds COMMAND ${CMAKE_COMMAND} -E remove ${DOTNET_OUTPUT_PATH}/${pkg}.${DOTNET_PACKAGE_VERSION}.symbols.nupkg) + ENDFOREACH() + LIST(APPEND build_dotnet_cmds COMMAND ${DOTNET_EXE} pack --no-build --no-restore ${DOTNET_PROJPATH} -c ${DOTNET_CONFIG} ${DOTNET_BUILD_PROPERTIES} ${DOTNET_PACK_OPTIONS}) + ELSE() + MESSAGE("-- Adding ${build_dotnet_type} project ${DOTNET_PROJPATH} (no nupkg)") + ENDIF() + + ADD_CUSTOM_COMMAND( + OUTPUT ${DOTNET_OUTPUTS} + DEPENDS ${DOTNET_deps} + ${build_dotnet_cmds} + ) + ADD_CUSTOM_TARGET( + BUILD_${DOTNET_PROJNAME} ALL + DEPENDS ${DOTNET_OUTPUTS}) + +ENDMACRO() + +FUNCTION(ADD_DOTNET DOTNET_PROJECT) + DOTNET_GET_DEPS(${DOTNET_PROJECT} "${ARGN}") + SET(DOTNET_IS_MSBUILD FALSE) + DOTNET_BUILD_COMMANDS() + ADD_DOTNET_DEPENDENCY_TARGETS(BUILD) +ENDFUNCTION() + +FUNCTION(ADD_MSBUILD DOTNET_PROJECT) + IF(NOT WIN32) + MESSAGE("-- Building non-Win32, skipping ${DOTNET_PROJECT}") + RETURN() + ENDIF() + + DOTNET_GET_DEPS(${DOTNET_PROJECT} "${ARGN}") + SET(DOTNET_IS_MSBUILD TRUE) + DOTNET_BUILD_COMMANDS() + ADD_DOTNET_DEPENDENCY_TARGETS(BUILD) +ENDFUNCTION() + +FUNCTION(RUN_DOTNET DOTNET_PROJECT) + DOTNET_GET_DEPS(${DOTNET_PROJECT} "${ARGN};NETCOREAPP") + MESSAGE("-- Adding dotnet run project ${DOTNET_PROJECT}") + FILE(MAKE_DIRECTORY ${DOTNET_OUTPUT_PATH}) + ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${DOTNET_PROJNAME}.runtimestamp ${DOTNET_RUN_OUTPUT} + DEPENDS ${DOTNET_deps} + COMMAND ${DOTNET_EXE} restore ${DOTNET_PROJPATH} ${DOTNET_IMPORT_PROPERTIES} + COMMAND ${DOTNET_EXE} clean ${DOTNET_PROJPATH} ${DOTNET_BUILD_PROPERTIES} + COMMAND ${DOTNET_EXE} build --no-restore ${DOTNET_PROJPATH} -c ${DOTNET_CONFIG} ${DOTNET_BUILD_PROPERTIES} ${DOTNET_BUILD_OPTIONS} + # XXX tfm + COMMAND ${DOTNET_EXE} ${DOTNET_OUTPUT_PATH}/netcoreapp2.0/${DOTNET_PROJNAME}.dll ${DOTNET_ARGUMENTS} + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${DOTNET_PROJNAME}.runtimestamp + WORKING_DIRECTORY ${DOTNET_OUTPUT_PATH}) + ADD_CUSTOM_TARGET( + RUN_${DOTNET_PROJNAME} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${DOTNET_PROJNAME}.runtimestamp ${DOTNET_RUN_OUTPUT}) + ADD_DOTNET_DEPENDENCY_TARGETS(RUN) +ENDFUNCTION() + +FUNCTION(TEST_DOTNET DOTNET_PROJECT) + DOTNET_GET_DEPS(${DOTNET_PROJECT} "${ARGN}") + MESSAGE("-- Adding dotnet test project ${DOTNET_PROJECT}") + IF(WIN32) + SET(test_framework_args "") + ELSE() + SET(test_framework_args -f netcoreapp2.0) + ENDIF() + + ADD_TEST(NAME ${DOTNET_PROJNAME} + COMMAND ${DOTNET_EXE} test ${test_framework_args} --results-directory "${CMAKE_BINARY_DIR}" --logger trx ${DOTNET_ARGUMENTS} + WORKING_DIRECTORY ${DOTNET_OUTPUT_PATH}) + +ENDFUNCTION() + +SET_PROPERTY(GLOBAL PROPERTY DOTNET_LAST_SMOKETEST "") + +FUNCTION(SMOKETEST_DOTNET DOTNET_PROJECT) + MESSAGE("-- Adding dotnet smoke test project ${DOTNET_PROJECT}") + IF(WIN32) + RUN_DOTNET(${DOTNET_PROJECT} "${ARGN}") + ELSE() + RUN_DOTNET(${DOTNET_PROJECT} "${ARGN}") + ENDIF() + + DOTNET_GET_DEPS(${DOTNET_PROJECT} "${ARGN}") + ADD_CUSTOM_TARGET( + SMOKETEST_${DOTNET_PROJNAME} + ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${DOTNET_PROJNAME}.runtimestamp) + ADD_DOTNET_DEPENDENCY_TARGETS(SMOKETEST) + GET_PROPERTY(_dn_last_smoketest GLOBAL PROPERTY DOTNET_LAST_SMOKETEST) + IF(_dn_last_smoketest) + MESSAGE("${_dn_last_smoketest} -> SMOKETEST_${DOTNET_PROJNAME}") + ADD_DEPENDENCIES(SMOKETEST_${DOTNET_PROJNAME} ${_dn_last_smoketest}) + ENDIF() + # Chain the smoke tests together so they are executed sequentially + SET_PROPERTY(GLOBAL PROPERTY DOTNET_LAST_SMOKETEST SMOKETEST_${DOTNET_PROJNAME}) + +ENDFUNCTION() + +SET(DOTNET_IMPORTS_TEMPLATE ${CMAKE_CURRENT_LIST_DIR}/DotnetImports.props.in) + +FUNCTION(GEN_DOTNET_PROPS target_props_file) + CMAKE_PARSE_ARGUMENTS( + # prefix + _DNP + # options (flags) + "" + # oneValueArgs + "PACKAGE_VERSION;XML_INJECT" + # multiValueArgs + "" + # the input arguments + ${ARGN}) + + IF(NOT _DNP_PACKAGE_VERSION) + SET(_DNP_PACKAGE_VERSION 1.0.0) + ENDIF() + + IF(_DNP_XML_INJECT) + SET(_DN_CUSTOM_BUILDPROPS ${_DNP_XML_INJECT}) + ENDIF() + + SET(_DN_OUTPUT_PATH ${CMAKE_BINARY_DIR}) + SET(_DN_XPLAT_LIB_DIR ${CMAKE_BINARY_DIR}) + SET(_DN_VERSION ${_DNP_PACKAGE_VERSION}) + CONFIGURE_FILE(${DOTNET_IMPORTS_TEMPLATE} ${target_props_file}) + UNSET(_DN_OUTPUT_PATH) + UNSET(_DN_XPLAT_LIB_DIR) + UNSET(_DN_VERSION) +ENDFUNCTION() + + +MESSAGE("-- Found .NET toolchain: ${DOTNET_EXE} (version ${DOTNET_VERSION})") +SET(DOTNET_FOUND TRUE) diff --git a/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile b/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile index 87e3c8d67..6012bb25f 100644 --- a/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile @@ -32,7 +32,6 @@ RUN apt-get update && \ libomp-dev \ llvm-3.9 \ make \ - mono-devel \ ninja-build \ python3 \ python3-setuptools \ @@ -48,4 +47,6 @@ 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 +# TODO .NET core does not support Linux x86 yet, disable it for now. +# see: https://github.com/dotnet/coreclr/issues/9265 +ENV ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-3.9/bin/llvm-symbolizer DOTNET_BINDINGS=0 diff --git a/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile b/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile index c963ce255..9c6bdc054 100644 --- a/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile @@ -2,9 +2,10 @@ FROM ubuntu:14.04 RUN apt-get update && \ apt-get -y --no-install-recommends install \ + apt-transport-https \ binutils \ clang-3.9 \ - cmake \ + curl \ doxygen \ default-jdk \ gcc-multilib \ @@ -18,13 +19,20 @@ RUN apt-get update && \ lib32gomp1 \ llvm-3.9 \ make \ - mono-devel \ ninja-build \ python3 \ python3-setuptools \ python2.7 \ python-setuptools +RUN curl -SL https://packages.microsoft.com/config/ubuntu/14.04/packages-microsoft-prod.deb --output packages-microsoft-prod.deb && \ + dpkg -i packages-microsoft-prod.deb && \ + apt-get update && \ + apt-get -y --no-install-recommends install dotnet-sdk-2.1 + +RUN curl -SL https://cmake.org/files/v3.12/cmake-3.12.0-Linux-x86_64.sh --output cmake-3.12.0-Linux-x86_64.sh && \ + sh cmake-3.12.0-Linux-x86_64.sh --prefix=/usr/local --exclude-subdir + # Create `user` user for container with password `user`. and give it # password-less sudo access RUN useradd -m user && \ diff --git a/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile b/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile index 08686e275..f4d9c873a 100644 --- a/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile @@ -2,10 +2,12 @@ FROM ubuntu:16.04 RUN apt-get update && \ apt-get -y --no-install-recommends install \ + apt-transport-https \ binutils \ clang \ clang-3.9 \ cmake \ + curl \ doxygen \ default-jdk \ gcc-multilib \ @@ -20,7 +22,6 @@ RUN apt-get update && \ libomp-dev \ llvm-3.9 \ make \ - mono-devel \ ninja-build \ python3 \ python3-setuptools \ @@ -28,6 +29,11 @@ RUN apt-get update && \ python-setuptools \ sudo +RUN curl -SL https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb --output packages-microsoft-prod.deb && \ + dpkg -i packages-microsoft-prod.deb && \ + apt-get update && \ + apt-get -y --no-install-recommends install dotnet-sdk-2.1 + # Create `user` user for container with password `user`. and give it # password-less sudo access RUN useradd -m user && \ diff --git a/contrib/ci/scripts/test_z3_examples_cmake.sh b/contrib/ci/scripts/test_z3_examples_cmake.sh index 687efebb4..a149a1d83 100755 --- a/contrib/ci/scripts/test_z3_examples_cmake.sh +++ b/contrib/ci/scripts/test_z3_examples_cmake.sh @@ -88,11 +88,14 @@ if [ "X${PYTHON_BINDINGS}" = "X1" ]; then fi if [ "X${DOTNET_BINDINGS}" = "X1" ]; then - # Build .NET example - # 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 run_non_native_binding mono ./dotnet_test.exe + if [ "X${ASAN_BUILD}" = "X1" ]; then + # The dotnet test get stuck on ASAN + # so don't run it for now. + echo "Skipping .NET example under ASan build" + else + run_quiet run_non_native_binding dotnet ${Z3_BUILD_DIR}/dotnet/netcoreapp2.0/dotnet.dll + fi fi if [ "X${JAVA_BINDINGS}" = "X1" ]; then diff --git a/contrib/qprofdiff/main.cpp b/contrib/qprofdiff/main.cpp index 58d21b77d..9a78249c2 100644 --- a/contrib/qprofdiff/main.cpp +++ b/contrib/qprofdiff/main.cpp @@ -78,10 +78,13 @@ int parse(string const & filename, map & data) { entry.num_instances = entry.max_generation = entry.max_cost = 0; } + // Existing entries represent previous occurrences of quantifiers + // that, at some point, were removed (e.g. backtracked). We sum + // up instances from all occurrences of the same qid. map_entry & entry = data[qid]; - entry.num_instances = atoi(tokens[1].c_str()); - entry.max_generation = (unsigned)atoi(tokens[2].c_str()); - entry.max_cost = (unsigned)atoi(tokens[3].c_str()); + entry.num_instances += atoi(tokens[1].c_str()); + entry.max_generation = max(entry.max_generation, (unsigned)atoi(tokens[2].c_str())); + entry.max_cost = max(entry.max_cost, (unsigned)atoi(tokens[3].c_str())); } } @@ -205,7 +208,7 @@ void diff(map & left, map & right) { } stable_sort(flat_data.begin(), flat_data.end(), - options.find("-si") != options.end() ? diff_item_lt_num_instances: + options.find("-si") != options.end() ? diff_item_lt_num_instances : options.find("-sg") != options.end() ? diff_item_lt_max_generation : options.find("-sc") != options.end() ? diff_item_lt_max_cost : diff_item_lt_num_instances); diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6c50320ed..5d06029f9 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -112,3 +112,10 @@ set_target_properties(z3_tptp5 PROPERTIES EXCLUDE_FROM_ALL TRUE) if (BUILD_PYTHON_BINDINGS) add_subdirectory(python) endif() + +################################################################################ +# Build dotnet examples +################################################################################ +if (BUILD_DOTNET_BINDINGS) + add_subdirectory(dotnet) +endif() \ No newline at end of file diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index ab9c73209..f93045c9d 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -1166,6 +1166,14 @@ static void parse_example() { // expr b = c.parse_string("(benchmark tst :extrafuns ((x Int) (y Int)) :formula (> x y) :formula (> x 0))"); } +static void parse_string() { + std::cout << "parse string\n"; + z3::context c; + z3::solver s(c); + s.from_string("(declare-const x Int)(assert (> x 10))"); + std::cout << s.check() << "\n"; +} + void mk_model_example() { context c; @@ -1252,6 +1260,7 @@ int main() { sudoku_example(); std::cout << "\n"; consequence_example(); std::cout << "\n"; parse_example(); std::cout << "\n"; + parse_string(); std::cout << "\n"; mk_model_example(); std::cout << "\n"; recfun_example(); std::cout << "\n"; std::cout << "done\n"; diff --git a/examples/c/test_capi.c b/examples/c/test_capi.c index 980592f25..f9c108b92 100644 --- a/examples/c/test_capi.c +++ b/examples/c/test_capi.c @@ -1697,7 +1697,7 @@ void parser_example3() LOG_MSG("parser_example3"); cfg = Z3_mk_config(); - /* See quantifer_example1 */ + /* See quantifier_example1 */ Z3_set_param_value(cfg, "model", "true"); ctx = mk_context_custom(cfg, error_handler); Z3_del_config(cfg); diff --git a/examples/dotnet/CMakeLists.txt b/examples/dotnet/CMakeLists.txt new file mode 100644 index 000000000..108326f83 --- /dev/null +++ b/examples/dotnet/CMakeLists.txt @@ -0,0 +1,34 @@ +find_package(Dotnet REQUIRED) + +if("${TARGET_ARCHITECTURE}" STREQUAL "i686") + set(Z3_DOTNET_PLATFORM "x86") +else() + set(Z3_DOTNET_PLATFORM "AnyCPU") +endif() + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dotnet.csproj dotnet.csproj COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Program.cs Program.cs COPYONLY) + +ADD_DOTNET(${CMAKE_CURRENT_BINARY_DIR}/dotnet.csproj + PLATFORM ${Z3_DOTNET_PLATFORM} + NETCOREAPP + CUSTOM_BUILDPROPS "${Z3_VERSION_MAJOR}.${Z3_VERSION_MINOR}.${Z3_VERSION_PATCH}" + DEPENDS Microsoft.Z3) + +if(UNIX AND NOT APPLE) + set(z3_dotnet_native_lib ${CMAKE_BINARY_DIR}/libz3.so) + set(z3_dotnet_test_manual_copy_deps + ${CMAKE_BINARY_DIR}/Microsoft.Z3/netstandard2.0/Microsoft.Z3.dll + ${z3_dotnet_native_lib} + ) + + add_custom_target( + z3_dotnet_test_manual_copy_assembly_hack ALL + COMMAND ${CMAKE_COMMAND} -E copy ${z3_dotnet_test_manual_copy_deps} ${CMAKE_BINARY_DIR}/dotnet/netcoreapp2.0/ + # hack the libz3 entry in deps so it's easy enough for dotnet to reach it... + COMMAND sed \"s/runtimes\\/.*libz3\\.so/libz3.so/\" -i ${CMAKE_BINARY_DIR}/dotnet/netcoreapp2.0/dotnet.deps.json + ) + + add_dependencies(z3_dotnet_test_manual_copy_assembly_hack BUILD_dotnet) +endif() + diff --git a/examples/dotnet/README b/examples/dotnet/README index 3f7e989df..b42156761 100644 --- a/examples/dotnet/README +++ b/examples/dotnet/README @@ -1,7 +1,6 @@ Small example using the .Net bindings. -This example is only built if you have Visual Studio. To build the example execute make examples in the build directory. -It will create the executable dotnet_example.exe +It will create a .net core 2.0 app. diff --git a/examples/dotnet/dotnet.csproj b/examples/dotnet/dotnet.csproj new file mode 100644 index 000000000..7776259ea --- /dev/null +++ b/examples/dotnet/dotnet.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp2.0 + + + + + + + diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs index f3a8f9f2c..4f8cdc759 100644 --- a/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs @@ -33,14 +33,14 @@ namespace Microsoft.SolverFoundation.Plugin.Z3 #region Solver construction and destruction - /// Constructor that initializes the base clases + /// Constructor that initializes the base classes public Z3MILPSolver() : base(null) { _result = LinearResult.Feasible; _solver = new Z3BaseSolver(this); } - /// Constructor that initializes the base clases + /// Constructor that initializes the base classes public Z3MILPSolver(ISolverEnvironment context) : this() { } /// diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs index 530df3394..de91c7b6e 100644 --- a/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs @@ -29,13 +29,13 @@ namespace Microsoft.SolverFoundation.Plugin.Z3 private NonlinearResult _result; private Z3BaseSolver _solver; - /// Constructor that initializes the base clases + /// Constructor that initializes the base classes public Z3TermSolver() : base(null) { _solver = new Z3BaseSolver(this); } - /// Constructor that initializes the base clases + /// Constructor that initializes the base classes public Z3TermSolver(ISolverEnvironment context) : this() { } /// diff --git a/examples/python/data/horn3.smt2 b/examples/python/data/horn3.smt2 new file mode 100644 index 000000000..873784e43 --- /dev/null +++ b/examples/python/data/horn3.smt2 @@ -0,0 +1,17 @@ +(declare-rel Invariant (Bool)) +(declare-rel Goal ()) +(declare-var l0 Bool) +(declare-var l2 Bool) +(declare-var l4 Bool) +(declare-var l6 Bool) +(declare-var l8 Bool) +(declare-var l10 Bool) +(rule (=> (not (or l4)) (Invariant l4))) +(rule (=> (and (Invariant l4) + (= (and (not l4) (not l2)) l6) + (= (and l4 l2) l8) + (= (and (not l8) (not l6)) l10) + ) (Invariant l10))) +(rule (=> (and (Invariant l4) + l4) Goal)) +(query Goal) diff --git a/examples/python/data/horn4.smt2 b/examples/python/data/horn4.smt2 new file mode 100644 index 000000000..0a64b41db --- /dev/null +++ b/examples/python/data/horn4.smt2 @@ -0,0 +1,99 @@ +(declare-rel Invariant (Bool Bool Bool Bool Bool Bool)) +(declare-rel Goal ()) +(declare-var l0 Bool) +(declare-var l2 Bool) +(declare-var l4 Bool) +(declare-var l6 Bool) +(declare-var l8 Bool) +(declare-var l10 Bool) +(declare-var l12 Bool) +(declare-var l14 Bool) +(declare-var l16 Bool) +(declare-var l18 Bool) +(declare-var l20 Bool) +(declare-var l22 Bool) +(declare-var l24 Bool) +(declare-var l26 Bool) +(declare-var l28 Bool) +(declare-var l30 Bool) +(declare-var l32 Bool) +(declare-var l34 Bool) +(declare-var l36 Bool) +(declare-var l38 Bool) +(declare-var l40 Bool) +(declare-var l42 Bool) +(declare-var l44 Bool) +(declare-var l46 Bool) +(declare-var l48 Bool) +(declare-var l50 Bool) +(declare-var l52 Bool) +(declare-var l54 Bool) +(declare-var l56 Bool) +(declare-var l58 Bool) +(declare-var l60 Bool) +(declare-var l62 Bool) +(declare-var l64 Bool) +(declare-var l66 Bool) +(declare-var l68 Bool) +(declare-var l70 Bool) +(declare-var l72 Bool) +(declare-var l74 Bool) +(declare-var l76 Bool) +(declare-var l78 Bool) +(declare-var l80 Bool) +(declare-var l82 Bool) +(declare-var l84 Bool) +(declare-var l86 Bool) +(rule (=> (not (or l4 l6 l8 l10 l12 l14)) (Invariant l4 l6 l8 l10 l12 l14))) +(rule (=> (and (Invariant l4 l6 l8 l10 l12 l14) + (= (and l6 (not l4)) l16) + (= (and l10 (not l8)) l18) + (= (and l18 l16) l20) + (= (and (not l14) (not l12)) l22) + (= (and l22 l20) l24) + (= (and (not l24) (not l4)) l26) + (= (and (not l6) l4) l28) + (= (and (not l28) (not l16)) l30) + (= (and (not l30) (not l24)) l32) + (= (and l6 l4) l34) + (= (and (not l34) l8) l36) + (= (and l34 (not l8)) l38) + (= (and (not l38) (not l36)) l40) + (= (and (not l40) (not l24)) l42) + (= (and l34 l8) l44) + (= (and (not l44) l10) l46) + (= (and l44 (not l10)) l48) + (= (and (not l48) (not l46)) l50) + (= (and (not l50) (not l24)) l52) + (= (and l10 l8) l54) + (= (and l54 l34) l56) + (= (and (not l56) l12) l58) + (= (and l56 (not l12)) l60) + (= (and (not l60) (not l58)) l62) + (= (and (not l62) (not l24)) l64) + (= (and l56 l12) l66) + (= (and (not l66) l14) l68) + (= (and l66 (not l14)) l70) + (= (and (not l70) (not l68)) l72) + (= (and (not l72) (not l24)) l74) + (= (and l6 l4) l76) + (= (and (not l76) l18) l78) + (= (and (not l78) l10) l80) + (= (and (not l80) l22) l82) + (= (and (not l82) (not l24)) l84) + (= (and l84 (not l0)) l86) + ) (Invariant l26 l32 l42 l52 l64 l74))) +(rule (=> (and (Invariant l4 l6 l8 l10 l12 l14) + (= (and l84 (not l0)) l86) + (= (and (not l82) (not l24)) l84) + (= (and (not l80) l22) l82) + (= (and (not l78) l10) l80) + (= (and (not l76) l18) l78) + (= (and l6 l4) l76) + (= (and l10 (not l8)) l18) + (= (and (not l14) (not l12)) l22) + (= (and l22 l20) l24) + (= (and l18 l16) l20) + (= (and l6 (not l4)) l16) + l86) Goal)) +(query Goal) diff --git a/examples/python/data/horn5.smt2 b/examples/python/data/horn5.smt2 new file mode 100644 index 000000000..37642d517 --- /dev/null +++ b/examples/python/data/horn5.smt2 @@ -0,0 +1,21 @@ +(declare-rel Invariant (Bool Bool Bool Bool)) +(declare-rel Goal ()) +(declare-var l0 Bool) +(declare-var l2 Bool) +(declare-var l4 Bool) +(declare-var l6 Bool) +(declare-var l8 Bool) +(declare-var l10 Bool) +(declare-var l12 Bool) +(declare-var l14 Bool) +(declare-var l16 Bool) +(rule (=> (not (or l4 l6 l8 l10)) (Invariant l4 l6 l8 l10))) +(rule (=> (and (Invariant l4 l6 l8 l10) + (= (and l6 l4) l12) + (= (and l12 l8) l14) + (= (and l10 (not l0)) l16) + ) (Invariant l12 l8 l0 l14))) +(rule (=> (and (Invariant l4 l6 l8 l10) + (= (and l10 (not l0)) l16) + l16) Goal)) +(query Goal) diff --git a/examples/python/data/horn6.smt2 b/examples/python/data/horn6.smt2 new file mode 100644 index 000000000..d90187e4d --- /dev/null +++ b/examples/python/data/horn6.smt2 @@ -0,0 +1,292 @@ +(declare-rel Invariant (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool)) +(declare-rel Goal ()) +(declare-var l0 Bool) +(declare-var l2 Bool) +(declare-var l4 Bool) +(declare-var l6 Bool) +(declare-var l8 Bool) +(declare-var l10 Bool) +(declare-var l12 Bool) +(declare-var l14 Bool) +(declare-var l16 Bool) +(declare-var l18 Bool) +(declare-var l20 Bool) +(declare-var l22 Bool) +(declare-var l24 Bool) +(declare-var l26 Bool) +(declare-var l28 Bool) +(declare-var l30 Bool) +(declare-var l32 Bool) +(declare-var l34 Bool) +(declare-var l36 Bool) +(declare-var l38 Bool) +(declare-var l40 Bool) +(declare-var l42 Bool) +(declare-var l44 Bool) +(declare-var l46 Bool) +(declare-var l48 Bool) +(declare-var l50 Bool) +(declare-var l52 Bool) +(declare-var l54 Bool) +(declare-var l56 Bool) +(declare-var l58 Bool) +(declare-var l60 Bool) +(declare-var l62 Bool) +(declare-var l64 Bool) +(declare-var l66 Bool) +(declare-var l68 Bool) +(declare-var l70 Bool) +(declare-var l72 Bool) +(declare-var l74 Bool) +(declare-var l76 Bool) +(declare-var l78 Bool) +(declare-var l80 Bool) +(declare-var l82 Bool) +(declare-var l84 Bool) +(declare-var l86 Bool) +(declare-var l88 Bool) +(declare-var l90 Bool) +(declare-var l92 Bool) +(declare-var l94 Bool) +(declare-var l96 Bool) +(declare-var l98 Bool) +(declare-var l100 Bool) +(declare-var l102 Bool) +(declare-var l104 Bool) +(declare-var l106 Bool) +(declare-var l108 Bool) +(declare-var l110 Bool) +(declare-var l112 Bool) +(declare-var l114 Bool) +(declare-var l116 Bool) +(declare-var l118 Bool) +(declare-var l120 Bool) +(declare-var l122 Bool) +(declare-var l124 Bool) +(declare-var l126 Bool) +(declare-var l128 Bool) +(declare-var l130 Bool) +(declare-var l132 Bool) +(declare-var l134 Bool) +(declare-var l136 Bool) +(declare-var l138 Bool) +(declare-var l140 Bool) +(declare-var l142 Bool) +(declare-var l144 Bool) +(declare-var l146 Bool) +(declare-var l148 Bool) +(declare-var l150 Bool) +(declare-var l152 Bool) +(declare-var l154 Bool) +(declare-var l156 Bool) +(declare-var l158 Bool) +(declare-var l160 Bool) +(declare-var l162 Bool) +(declare-var l164 Bool) +(declare-var l166 Bool) +(declare-var l168 Bool) +(declare-var l170 Bool) +(declare-var l172 Bool) +(declare-var l174 Bool) +(declare-var l176 Bool) +(declare-var l178 Bool) +(declare-var l180 Bool) +(declare-var l182 Bool) +(declare-var l184 Bool) +(declare-var l186 Bool) +(declare-var l188 Bool) +(declare-var l190 Bool) +(declare-var l192 Bool) +(declare-var l194 Bool) +(declare-var l196 Bool) +(declare-var l198 Bool) +(declare-var l200 Bool) +(declare-var l202 Bool) +(declare-var l204 Bool) +(declare-var l206 Bool) +(declare-var l208 Bool) +(declare-var l210 Bool) +(declare-var l212 Bool) +(declare-var l214 Bool) +(declare-var l216 Bool) +(declare-var l218 Bool) +(declare-var l220 Bool) +(declare-var l222 Bool) +(declare-var l224 Bool) +(declare-var l226 Bool) +(declare-var l228 Bool) +(declare-var l230 Bool) +(declare-var l232 Bool) +(declare-var l234 Bool) +(declare-var l236 Bool) +(declare-var l238 Bool) +(declare-var l240 Bool) +(declare-var l242 Bool) +(declare-var l244 Bool) +(declare-var l246 Bool) +(declare-var l248 Bool) +(declare-var l250 Bool) +(declare-var l252 Bool) +(declare-var l254 Bool) +(declare-var l256 Bool) +(declare-var l258 Bool) +(declare-var l260 Bool) +(declare-var l262 Bool) +(declare-var l264 Bool) +(declare-var l266 Bool) +(declare-var l268 Bool) +(declare-var l270 Bool) +(declare-var l272 Bool) +(declare-var l274 Bool) +(declare-var l276 Bool) +(declare-var l278 Bool) +(declare-var l280 Bool) +(declare-var l282 Bool) +(declare-var l284 Bool) +(declare-var l286 Bool) +(declare-var l288 Bool) +(declare-var l290 Bool) +(declare-var l292 Bool) +(declare-var l294 Bool) +(declare-var l296 Bool) +(declare-var l298 Bool) +(declare-var l300 Bool) +(declare-var l302 Bool) +(declare-var l304 Bool) +(declare-var l306 Bool) +(declare-var l308 Bool) +(declare-var l310 Bool) +(declare-var l312 Bool) +(declare-var l314 Bool) +(declare-var l316 Bool) +(rule (=> (not (or l8 l10 l12 l14 l16 l18 l20 l22 l24 l26 l28 l30 l32 l34 l36 l38 l40 l42 l44 l46 l48 l50 l52 l54 l56 l58 l60 l62 l64 l66 l68 l70 l72 l74)) (Invariant l8 l10 l12 l14 l16 l18 l20 l22 l24 l26 l28 l30 l32 l34 l36 l38 l40 l42 l44 l46 l48 l50 l52 l54 l56 l58 l60 l62 l64 l66 l68 l70 l72 l74))) +(rule (=> (and (Invariant l8 l10 l12 l14 l16 l18 l20 l22 l24 l26 l28 l30 l32 l34 l36 l38 l40 l42 l44 l46 l48 l50 l52 l54 l56 l58 l60 l62 l64 l66 l68 l70 l72 l74) + (= (and (not l20) (not l14)) l76) + (= (and (not l76) l8) l78) + (= (and l20 l14) l80) + (= (and (not l80) (not l78)) l82) + (= (and (not l28) l8) l84) + (= (and (not l84) l10) l86) + (= (and l18 l12) l88) + (= (and l88 l38) l90) + (= (and (not l24) (not l8)) l92) + (= (and l92 (not l26)) l94) + (= (and l94 l28) l96) + (= (and l96 (not l90)) l98) + (= (and (not l98) (not l86)) l100) + (= (and l38 l18) l102) + (= (and l102 l12) l104) + (= (and (not l104) (not l26)) l106) + (= (and l24 (not l16)) l108) + (= (and l108 (not l32)) l110) + (= (and l110 l106) l112) + (= (and (not l32) l14) l114) + (= (and (not l114) (not l112)) l116) + (= (and (not l114) l16) l118) + (= (and l32 (not l14)) l120) + (= (and l120 l106) l122) + (= (and l122 l24) l124) + (= (and (not l124) (not l118)) l126) + (= (and l26 (not l22)) l128) + (= (and l128 (not l36)) l130) + (= (and (not l36) l20) l132) + (= (and l130 (not l90)) l134) + (= (and (not l134) (not l132)) l136) + (= (and (not l132) l22) l138) + (= (and l26 (not l20)) l140) + (= (and l140 l36) l142) + (= (and l142 (not l90)) l144) + (= (and (not l144) (not l138)) l146) + (= (and (not l106) l24) l148) + (= (and l106 (not l24)) l150) + (= (and (not l150) (not l148)) l152) + (= (and (not l90) l24) l154) + (= (and l90 l26) l156) + (= (and (not l156) (not l154)) l158) + (= (and (not l30) l2) l160) + (= (and l28 (not l2)) l162) + (= (and (not l162) (not l160)) l164) + (= (and l28 l2) l166) + (= (and (not l166) l30) l168) + (= (and (not l30) l28) l170) + (= (and l170 l8) l172) + (= (and (not l172) (not l168)) l174) + (= (and (not l34) l4) l176) + (= (and l32 (not l4)) l178) + (= (and (not l178) (not l176)) l180) + (= (and l32 l4) l182) + (= (and (not l182) l34) l184) + (= (and (not l34) l32) l186) + (= (and l186 l14) l188) + (= (and (not l188) (not l184)) l190) + (= (and (not l40) l6) l192) + (= (and l36 (not l6)) l194) + (= (and (not l194) (not l192)) l196) + (= (and (not l24) (not l10)) l198) + (= (and l198 (not l26)) l200) + (= (and l200 (not l28)) l202) + (= (and l202 (not l90)) l204) + (= (and (not l204) (not l84)) l206) + (= (and l36 l6) l208) + (= (and (not l208) l40) l210) + (= (and (not l40) l36) l212) + (= (and l212 l20) l214) + (= (and (not l214) (not l210)) l216) + (= (and l62 l44) l218) + (= (and l52 l46) l220) + (= (and l220 l72) l222) + (= (and (not l60) (not l58)) l224) + (= (and l224 l62) l226) + (= (and l226 (not l222)) l228) + (= (and (not l228) (not l218)) l230) + (= (and (not l222) (not l60)) l232) + (= (and (not l66) l58) l234) + (= (and (not l66) l48) l236) + (= (and l234 l232) l238) + (= (and (not l238) (not l236)) l240) + (= (and l66 l50) l242) + (= (and l66 (not l48)) l244) + (= (and l244 l232) l246) + (= (and l246 l58) l248) + (= (and (not l248) (not l242)) l250) + (= (and (not l70) l60) l252) + (= (and (not l70) l54) l254) + (= (and l252 (not l222)) l256) + (= (and (not l256) (not l254)) l258) + (= (and l70 l56) l260) + (= (and l70 l60) l262) + (= (and l262 (not l222)) l264) + (= (and (not l264) (not l260)) l266) + (= (and (not l232) l58) l268) + (= (and l232 (not l58)) l270) + (= (and (not l270) (not l268)) l272) + (= (and l222 l60) l274) + (= (and (not l222) l58) l276) + (= (and (not l276) (not l274)) l278) + (= (and l62 (not l2)) l280) + (= (and (not l64) l2) l282) + (= (and (not l282) (not l280)) l284) + (= (and l62 l42) l286) + (= (and l286 (not l284)) l288) + (= (and l66 (not l4)) l290) + (= (and (not l68) l4) l292) + (= (and (not l292) (not l290)) l294) + (= (and (not l244) l66) l296) + (= (and l296 (not l294)) l298) + (= (and l70 (not l6)) l300) + (= (and (not l74) l6) l302) + (= (and (not l302) (not l300)) l304) + (= (and l224 (not l62)) l306) + (= (and (not l62) l42) l308) + (= (and l306 (not l222)) l310) + (= (and (not l310) (not l308)) l312) + (= (and l70 l54) l314) + (= (and l314 (not l304)) l316) + ) (Invariant l86 l100 l116 l118 l126 l136 l138 l146 l152 l158 l164 l174 l180 l190 l196 l206 l216 l218 l230 l240 l242 l250 l258 l260 l266 l272 l278 l284 l288 l294 l298 l304 l312 l316))) +(rule (=> (and (Invariant l8 l10 l12 l14 l16 l18 l20 l22 l24 l26 l28 l30 l32 l34 l36 l38 l40 l42 l44 l46 l48 l50 l52 l54 l56 l58 l60 l62 l64 l66 l68 l70 l72 l74) + (= (and (not l80) (not l78)) l82) + (= (and l20 l14) l80) + (= (and (not l76) l8) l78) + (= (and (not l20) (not l14)) l76) + (not l82)) Goal)) +(query Goal) diff --git a/examples/python/example.py b/examples/python/example.py index 761ae10be..b93ed6abf 100644 --- a/examples/python/example.py +++ b/examples/python/example.py @@ -2,7 +2,7 @@ # The Z3 Python API requires libz3.dll/.so/.dylib in the # PATH/LD_LIBRARY_PATH/DYLD_LIBRARY_PATH -# environment variable and the PYTHON_PATH environment variable +# environment variable and the PYTHONPATH environment variable # needs to point to the `python' directory that contains `z3/z3.py' # (which is at bin/python in our binary releases). diff --git a/examples/python/mini_ic3.py b/examples/python/mini_ic3.py index 5a8ea566b..b7b732459 100644 --- a/examples/python/mini_ic3.py +++ b/examples/python/mini_ic3.py @@ -17,6 +17,7 @@ class Horn2Transitions: def __init__(self): self.trans = True self.init = True + self.inputs = [] self.goal = True self.index = 0 @@ -48,6 +49,9 @@ class Horn2Transitions: if pred is None: return False self.goal = self.subst_vars("x", inv, pred) + self.goal = self.subst_vars("i", self.goal, self.goal) + self.inputs += self.vars + self.inputs = list(set(self.inputs)) return True def is_body(self, body): @@ -77,6 +81,9 @@ class Horn2Transitions: self.xs = self.vars pred = self.subst_vars("xn", inv1, pred) self.xns = self.vars + pred = self.subst_vars("i", pred, pred) + self.inputs += self.vars + self.inputs = list(set(self.inputs)) self.trans = pred return True @@ -97,12 +104,24 @@ class Horn2Transitions: def mk_subst(self, prefix, inv): self.index = 0 - return [(f, self.mk_bool(prefix)) for f in inv.children()] + if self.is_inv(inv) is not None: + return [(f, self.mk_bool(prefix)) for f in inv.children()] + else: + vars = self.get_vars(inv) + return [(f, self.mk_bool(prefix)) for f in vars] def mk_bool(self, prefix): self.index += 1 return Bool("%s%d" % (prefix, self.index)) + def get_vars(self, f, rs=[]): + if is_var(f): + return z3util.vset(rs + [f], str) + else: + for f_ in f.children(): + rs = self.get_vars(f_, rs) + return z3util.vset(rs, str) + # Produce a finite domain solver. # The theory QF_FD covers bit-vector formulas # and pseudo-Boolean constraints. @@ -169,8 +188,9 @@ def prune(R): return R - removed class MiniIC3: - def __init__(self, init, trans, goal, x0, xn): + def __init__(self, init, trans, goal, x0, inputs, xn): self.x0 = x0 + self.inputs = inputs self.xn = xn self.init = init self.bad = goal @@ -229,6 +249,9 @@ class MiniIC3: def project0(self, m): return self.values2literals(m, self.x0) + def projectI(self, m): + return self.values2literals(m, self.inputs) + def projectN(self, m): return self.values2literals(m, self.xn) @@ -242,12 +265,14 @@ class MiniIC3: is_sat = self.s_bad.check() if is_sat == sat: m = self.s_bad.model() - props = self.project0(m) + cube = self.project0(m) + props = cube + self.projectI(m) self.s_good.push() self.s_good.add(R) is_sat2 = self.s_good.check(props) assert is_sat2 == unsat core = self.s_good.unsat_core() + core = [c for c in core if c in set(cube)] self.s_good.pop() self.s_bad.pop() return is_sat, core @@ -263,8 +288,8 @@ class MiniIC3: # minimize cube that is core of Dual solver. # this assumes that props & cube => Trans - def minimize_cube(self, cube, lits): - is_sat = self.min_cube_solver.check(lits + [c for c in cube]) + def minimize_cube(self, cube, inputs, lits): + is_sat = self.min_cube_solver.check(lits + [c for c in cube] + [i for i in inputs]) assert is_sat == unsat core = self.min_cube_solver.unsat_core() assert core @@ -306,7 +331,9 @@ class MiniIC3: def generalize(self, cube, f): s = self.states[f - 1].solver if unsat == s.check(cube): - return s.unsat_core(), f + core = s.unsat_core() + if not check_disjoint(self.init, self.prev(And(core))): + return core, f return cube, f # Check if the negation of cube is inductive at level f @@ -319,7 +346,7 @@ class MiniIC3: m = s.model() s.pop() if is_sat == sat: - cube = self.next(self.minimize_cube(self.project0(m), self.projectN(m))) + cube = self.next(self.minimize_cube(self.project0(m), self.projectI(m), self.projectN(m))) elif is_sat == unsat: cube, f = self.generalize(cube, f) return cube, f, is_sat @@ -348,7 +375,7 @@ class MiniIC3: def test(file): h2t = Horn2Transitions() h2t.parse(file) - mp = MiniIC3(h2t.init, h2t.trans, h2t.goal, h2t.xs, h2t.xns) + mp = MiniIC3(h2t.init, h2t.trans, h2t.goal, h2t.xs, h2t.inputs, h2t.xns) result = mp.run() if isinstance(result, Goal): g = result @@ -364,75 +391,7 @@ def test(file): test("data/horn1.smt2") test("data/horn2.smt2") - - - -""" -# TBD: Quip variant of IC3 - -must = True -may = False - -class QGoal: - def __init__(self, cube, parent, level, must): - self.level = level - self.cube = cube - self.parent = parent - self.must = must - -class Quip(MiniIC3): - - # prev & tras -> r', such that r' intersects with cube - def add_reachable(self, prev, cube): - s = fd_solver() - s.add(self.trans) - s.add(prev) - s.add(Or(cube)) - is_sat = s.check() - assert is_sat == sat - m = s.model(); - result = self.values2literals(m, cube) - assert result - self.reachable.add(result) - - # A state s0 and level f0 such that - # not(s0) is f0-1 inductive - def quip_blocked(self, s0, f0): - self.push_heap(QGoal(self.next(s0), None, f0, must)) - while self.goals: - f, g = heapq.heappop(self.goals) - sys.stdout.write("%d." % f) - sys.stdout.flush() - if f == 0: - if g.must: - print("") - return g - self.add_reachable(self.init, p.parent.cube) - continue - - # TBD - return None - - - def run(self): - if not check_disjoint(self.init, self.bad): - return "goal is reached in initial state" - level = 0 - while True: - inv = self.is_valid() - if inv is not None: - return inv - is_sat, cube = self.unfold() - if is_sat == unsat: - level += 1 - print("Unfold %d" % level) - sys.stdout.flush() - self.add_solver() - elif is_sat == sat: - cex = self.quipie_blocked(cube, level) - if cex is not None: - return cex - else: - return is_sat - -""" \ No newline at end of file +test("data/horn3.smt2") +test("data/horn4.smt2") +test("data/horn5.smt2") +# test("data/horn6.smt2") # takes long time to finish diff --git a/examples/python/mini_quip.py b/examples/python/mini_quip.py new file mode 100644 index 000000000..a10d5a334 --- /dev/null +++ b/examples/python/mini_quip.py @@ -0,0 +1,786 @@ +from z3 import * +import heapq +import numpy +import time +import random + +verbose = True + +# Simplistic (and fragile) converter from +# a class of Horn clauses corresponding to +# a transition system into a transition system +# representation as +# It assumes it is given three Horn clauses +# of the form: +# init(x) => Invariant(x) +# Invariant(x) and trans(x,x') => Invariant(x') +# Invariant(x) and goal(x) => Goal(x) +# where Invariant and Goal are uninterpreted predicates + +class Horn2Transitions: + def __init__(self): + self.trans = True + self.init = True + self.inputs = [] + self.goal = True + self.index = 0 + + def parse(self, file): + fp = Fixedpoint() + goals = fp.parse_file(file) + for r in fp.get_rules(): + if not is_quantifier(r): + continue + b = r.body() + if not is_implies(b): + continue + f = b.arg(0) + g = b.arg(1) + if self.is_goal(f, g): + continue + if self.is_transition(f, g): + continue + if self.is_init(f, g): + continue + + def is_pred(self, p, name): + return is_app(p) and p.decl().name() == name + + def is_goal(self, body, head): + if not self.is_pred(head, "Goal"): + return False + pred, inv = self.is_body(body) + if pred is None: + return False + self.goal = self.subst_vars("x", inv, pred) + self.goal = self.subst_vars("i", self.goal, self.goal) + self.inputs += self.vars + self.inputs = list(set(self.inputs)) + return True + + def is_body(self, body): + if not is_and(body): + return None, None + fmls = [f for f in body.children() if self.is_inv(f) is None] + inv = None + for f in body.children(): + if self.is_inv(f) is not None: + inv = f; + break + return And(fmls), inv + + def is_inv(self, f): + if self.is_pred(f, "Invariant"): + return f + return None + + def is_transition(self, body, head): + pred, inv0 = self.is_body(body) + if pred is None: + return False + inv1 = self.is_inv(head) + if inv1 is None: + return False + pred = self.subst_vars("x", inv0, pred) + self.xs = self.vars + pred = self.subst_vars("xn", inv1, pred) + self.xns = self.vars + pred = self.subst_vars("i", pred, pred) + self.inputs += self.vars + self.inputs = list(set(self.inputs)) + self.trans = pred + return True + + def is_init(self, body, head): + for f in body.children(): + if self.is_inv(f) is not None: + return False + inv = self.is_inv(head) + if inv is None: + return False + self.init = self.subst_vars("x", inv, body) + return True + + def subst_vars(self, prefix, inv, fml): + subst = self.mk_subst(prefix, inv) + self.vars = [ v for (k,v) in subst ] + return substitute(fml, subst) + + def mk_subst(self, prefix, inv): + self.index = 0 + if self.is_inv(inv) is not None: + return [(f, self.mk_bool(prefix)) for f in inv.children()] + else: + vars = self.get_vars(inv) + return [(f, self.mk_bool(prefix)) for f in vars] + + def mk_bool(self, prefix): + self.index += 1 + return Bool("%s%d" % (prefix, self.index)) + + def get_vars(self, f, rs=[]): + if is_var(f): + return z3util.vset(rs + [f], str) + else: + for f_ in f.children(): + rs = self.get_vars(f_, rs) + return z3util.vset(rs, str) + +# Produce a finite domain solver. +# The theory QF_FD covers bit-vector formulas +# and pseudo-Boolean constraints. +# By default cardinality and pseudo-Boolean +# constraints are converted to clauses. To override +# this default for cardinality constraints +# we set sat.cardinality.solver to True + +def fd_solver(): + s = SolverFor("QF_FD") + s.set("sat.cardinality.solver", True) + return s + + +# negate, avoid double negation +def negate(f): + if is_not(f): + return f.arg(0) + else: + return Not(f) + +def cube2clause(cube): + return Or([negate(f) for f in cube]) + +class State: + def __init__(self, s): + self.R = set([]) + self.solver = s + + def add(self, clause): + if clause not in self.R: + self.R |= { clause } + self.solver.add(clause) + +def is_seq(f): + return isinstance(f, list) or isinstance(f, tuple) or isinstance(f, AstVector) + +# Check if the initial state is bad +def check_disjoint(a, b): + s = fd_solver() + s.add(a) + s.add(b) + return unsat == s.check() + + +# Remove clauses that are subsumed +def prune(R): + removed = set([]) + s = fd_solver() + for f1 in R: + s.push() + for f2 in R: + if f2 not in removed: + s.add(Not(f2) if f1.eq(f2) else f2) + if s.check() == unsat: + removed |= { f1 } + s.pop() + return R - removed + +# Quip variant of IC3 + +must = True +may = False + +class QLemma: + def __init__(self, c): + self.cube = c + self.clause = cube2clause(c) + self.bad = False + + def __hash__(self): + return hash(tuple(set(self.cube))) + + def __eq__(self, qlemma2): + if set(self.cube) == set(qlemma2.cube) and self.bad == qlemma2.bad: + return True + else: + return False + + def __ne__(): + if not self.__eq__(self, qlemma2): + return True + else: + return False + + +class QGoal: + def __init__(self, cube, parent, level, must, encounter): + self.level = level + self.cube = cube + self.parent = parent + self.must = must + + def __lt__(self, other): + return self.level < other.level + + +class QReach: + + # it is assumed that there is a single initial state + # with all latches set to 0 in hardware design, so + # here init will always give a state where all variable are set to 0 + def __init__(self, init, xs): + self.xs = xs + self.constant_xs = [Not(x) for x in self.xs] + s = fd_solver() + s.add(init) + is_sat = s.check() + assert is_sat == sat + m = s.model() + # xs is a list, "for" will keep the order when iterating + self.states = numpy.array([[False for x in self.xs]]) # all set to False + assert not numpy.max(self.states) # since all element is False, so maximum should be False + + # check if new state exists + def is_exist(self, state): + if state in self.states: + return True + return False + + def enumerate(self, i, state_b, state): + while i < len(state) and state[i] not in self.xs: + i += 1 + if i >= len(state): + if state_b.tolist() not in self.states.tolist(): + self.states = numpy.append(self.states, [state_b], axis = 0) + return state_b + else: + return None + state_b[i] = False + if self.enumerate(i+1, state_b, state) is not None: + return state_b + else: + state_b[i] = True + return self.enumerate(i+1, state_b, state) + + def is_full_state(self, state): + for i in range(len(self.xs)): + if state[i] in self.xs: + return False + return True + + def add(self, cube): + state = self.cube2partial_state(cube) + assert len(state) == len(self.xs) + if not self.is_exist(state): + return None + if self.is_full_state(state): + self.states = numpy.append(self.states, [state], axis = 0) + else: + # state[i] is instance, state_b[i] is boolean + state_b = numpy.array(state) + for i in range(len(state)): # state is of same length as self.xs + # i-th literal in state hasn't been assigned value + # init un-assigned literals in state_b as True + # make state_b only contain boolean value + if state[i] in self.xs: + state_b[i] = True + else: + state_b[i] = is_true(state[i]) + if self.enumerate(0, state_b, state) is not None: + lits_to_remove = set([negate(f) for f in list(set(cube) - set(self.constant_xs))]) + self.constant_xs = list(set(self.constant_xs) - lits_to_remove) + return state + return None + + + def cube2partial_state(self, cube): + s = fd_solver() + s.add(And(cube)) + is_sat = s.check() + assert is_sat == sat + m = s.model() + state = numpy.array([m.eval(x) for x in self.xs]) + return state + + + def state2cube(self, s): + result = copy.deepcopy(self.xs) # x1, x2, ... + for i in range(len(self.xs)): + if not s[i]: + result[i] = Not(result[i]) + return result + + def intersect(self, cube): + state = self.cube2partial_state(cube) + mask = True + for i in range(len(self.xs)): + if is_true(state[i]) or is_false(state[i]): + mask = (self.states[:, i] == state[i]) & mask + intersects = numpy.reshape(self.states[mask], (-1, len(self.xs))) + if intersects.size > 0: + return And(self.state2cube(intersects[0])) # only need to return one single intersect + return None + + +class Quip: + + def __init__(self, init, trans, goal, x0, inputs, xn): + self.x0 = x0 + self.inputs = inputs + self.xn = xn + self.init = init + self.bad = goal + self.trans = trans + self.min_cube_solver = fd_solver() + self.min_cube_solver.add(Not(trans)) + self.goals = [] + s = State(fd_solver()) + s.add(init) + s.solver.add(trans) # check if a bad state can be reached in one step from current level + self.states = [s] + self.s_bad = fd_solver() + self.s_good = fd_solver() + self.s_bad.add(self.bad) + self.s_good.add(Not(self.bad)) + self.reachable = QReach(self.init, x0) + self.frames = [] # frames is a 2d list, each row (representing level) is a set containing several (clause, bad) pairs + self.count_may = 0 + + def next(self, f): + if is_seq(f): + return [self.next(f1) for f1 in f] + return substitute(f, zip(self.x0, self.xn)) + + def prev(self, f): + if is_seq(f): + return [self.prev(f1) for f1 in f] + return substitute(f, zip(self.xn, self.x0)) + + def add_solver(self): + s = fd_solver() + s.add(self.trans) + self.states += [State(s)] + + def R(self, i): + return And(self.states[i].R) + + def value2literal(self, m, x): + value = m.eval(x) + if is_true(value): + return x + if is_false(value): + return Not(x) + return None + + def values2literals(self, m, xs): + p = [self.value2literal(m, x) for x in xs] + return [x for x in p if x is not None] + + def project0(self, m): + return self.values2literals(m, self.x0) + + def projectI(self, m): + return self.values2literals(m, self.inputs) + + def projectN(self, m): + return self.values2literals(m, self.xn) + + + # Block a cube by asserting the clause corresponding to its negation + def block_cube(self, i, cube): + self.assert_clause(i, cube2clause(cube)) + + # Add a clause to levels 1 until i + def assert_clause(self, i, clause): + for j in range(1, i + 1): + self.states[j].add(clause) + assert str(self.states[j].solver) != str([False]) + + + # minimize cube that is core of Dual solver. + # this assumes that props & cube => Trans + # which means props & cube can only give us a Tr in Trans, + # and it will never make !Trans sat + def minimize_cube(self, cube, inputs, lits): + # min_cube_solver has !Trans (min_cube.solver.add(!Trans)) + is_sat = self.min_cube_solver.check(lits + [c for c in cube] + [i for i in inputs]) + assert is_sat == unsat + # unsat_core gives us some lits which make Tr sat, + # so that we can ignore other lits and include more states + core = self.min_cube_solver.unsat_core() + assert core + return [c for c in core if c in set(cube)] + + # push a goal on a heap + def push_heap(self, goal): + heapq.heappush(self.goals, (goal.level, goal)) + + + # make sure cube to be blocked excludes all reachable states + def check_reachable(self, cube): + s = fd_solver() + for state in self.reachable.states: + s.push() + r = self.reachable.state2cube(state) + s.add(And(self.prev(r))) + s.add(self.prev(cube)) + is_sat = s.check() + s.pop() + if is_sat == sat: + # if sat, it means the cube to be blocked contains reachable states + # so it is an invalid cube + return False + # if all fail, is_sat will be unsat + return True + + # Rudimentary generalization: + # If the cube is already unsat with respect to transition relation + # extract a core (not necessarily minimal) + # otherwise, just return the cube. + def generalize(self, cube, f): + s = self.states[f - 1].solver + if unsat == s.check(cube): + core = s.unsat_core() + if self.check_reachable(core): + return core, f + return cube, f + + + def valid_reachable(self, level): + s = fd_solver() + s.add(self.init) + for i in range(level): + s.add(self.trans) + for state in self.reachable.states: + s.push() + s.add(And(self.next(self.reachable.state2cube(state)))) + print self.reachable.state2cube(state) + print s.check() + s.pop() + + def lemmas(self, level): + return [(l.clause, l.bad) for l in self.frames[level]] + + # whenever a new reachable state is found, we use it to mark some existing lemmas as bad lemmas + def mark_bad_lemmas(self, new): + s = fd_solver() + reset = False + for frame in self.frames: + for lemma in frame: + s.push() + s.add(lemma.clause) + is_sat = s.check(new) + if is_sat == unsat: + reset = True + lemma.bad = True + s.pop() + if reset: + self.states = [self.states[0]] + for i in range(1, len(self.frames)): + self.add_solver() + for lemma in self.frames[i]: + if not lemma.bad: + self.states[i].add(lemma.clause) + + # prev & tras -> r', such that r' intersects with cube + def add_reachable(self, prev, cube): + s = fd_solver() + s.add(self.trans) + s.add(prev) + s.add(self.next(And(cube))) + is_sat = s.check() + assert is_sat == sat + m = s.model() + new = self.projectN(m) + state = self.reachable.add(self.prev(new)) # always add as non-primed + if state is not None: # if self.states do not have new state yet + self.mark_bad_lemmas(self.prev(new)) + + + # Check if the negation of cube is inductive at level f + def is_inductive(self, f, cube): + s = self.states[f - 1].solver + s.push() + s.add(self.prev(Not(And(cube)))) + is_sat = s.check(cube) + if is_sat == sat: + m = s.model() + s.pop() + if is_sat == sat: + cube = self.next(self.minimize_cube(self.project0(m), self.projectI(m), self.projectN(m))) + elif is_sat == unsat: + cube, f = self.generalize(cube, f) + cube = self.next(cube) + return cube, f, is_sat + + + # Determine if there is a cube for the current state + # that is potentially reachable. + def unfold(self, level): + core = [] + self.s_bad.push() + R = self.R(level) + self.s_bad.add(R) # check if current frame intersects with bad states, no trans + is_sat = self.s_bad.check() + if is_sat == sat: + m = self.s_bad.model() + cube = self.project0(m) + props = cube + self.projectI(m) + self.s_good.push() + self.s_good.add(R) + is_sat2 = self.s_good.check(props) + assert is_sat2 == unsat + core = self.s_good.unsat_core() + assert core + core = [c for c in core if c in set(cube)] + self.s_good.pop() + self.s_bad.pop() + return is_sat, core + + # A state s0 and level f0 such that + # not(s0) is f0-1 inductive + def quip_blocked(self, s0, f0): + self.push_heap(QGoal(self.next(s0), None, f0, must, 0)) + while self.goals: + f, g = heapq.heappop(self.goals) + sys.stdout.write("%d." % f) + if not g.must: + self.count_may -= 1 + sys.stdout.flush() + if f == 0: + if g.must: + s = fd_solver() + s.add(self.init) + s.add(self.prev(g.cube)) + # since init is a complete assignment, so g.cube must equal to init in sat solver + assert is_sat == s.check() + if verbose: + print("") + return g + self.add_reachable(self.init, g.parent.cube) + continue + + r0 = self.reachable.intersect(self.prev(g.cube)) + if r0 is not None: + if g.must: + if verbose: + print "" + s = fd_solver() + s.add(self.trans) + # make it as a concrete reachable state + # intersect returns an And(...), so use children to get cube list + g.cube = r0.children() + while True: + is_sat = s.check(self.next(g.cube)) + assert is_sat == sat + r = self.next(self.project0(s.model())) + r = self.reachable.intersect(self.prev(r)) + child = QGoal(self.next(r.children()), g, 0, g.must, 0) + g = child + if not check_disjoint(self.init, self.prev(g.cube)): + # g is init, break the loop + break + init = g + while g.parent is not None: + g.parent.level = g.level + 1 + g = g.parent + return init + if g.parent is not None: + self.add_reachable(r0, g.parent.cube) + continue + + cube = None + is_sat = sat + f_1 = len(self.frames) - 1 + while f_1 >= f: + for l in self.frames[f_1]: + if not l.bad and len(l.cube) > 0 and set(l.cube).issubset(g.cube): + cube = l.cube + is_sat == unsat + break + f_1 -= 1 + if cube is None: + cube, f_1, is_sat = self.is_inductive(f, g.cube) + if is_sat == unsat: + self.frames[f_1].add(QLemma(self.prev(cube))) + self.block_cube(f_1, self.prev(cube)) + if f_1 < f0: + # learned clause might also be able to block same bad states in higher level + if set(list(cube)) != set(list(g.cube)): + self.push_heap(QGoal(cube, None, f_1 + 1, may, 0)) + self.count_may += 1 + else: + # re-queue g.cube in higher level, here g.parent is simply for tracking down the trace when output. + self.push_heap(QGoal(g.cube, g.parent, f_1 + 1, g.must, 0)) + if not g.must: + self.count_may += 1 + else: + # qcube is a predecessor of g + qcube = QGoal(cube, g, f_1 - 1, g.must, 0) + if not g.must: + self.count_may += 1 + self.push_heap(qcube) + + if verbose: + print("") + return None + + # Check if there are two states next to each other that have the same clauses. + def is_valid(self): + i = 1 + inv = None + while True: + # self.states[].R contains full lemmas + # self.frames[] contains delta-encoded lemmas + while len(self.states) <= i+1: + self.add_solver() + while len(self.frames) <= i+1: + self.frames.append(set()) + duplicates = set([]) + for l in self.frames[i+1]: + if l in self.frames[i]: + duplicates |= {l} + self.frames[i] = self.frames[i] - duplicates + pushed = set([]) + for l in (self.frames[i] - self.frames[i+1]): + if not l.bad: + s = self.states[i].solver + s.push() + s.add(self.next(Not(l.clause))) + s.add(l.clause) + is_sat = s.check() + s.pop() + if is_sat == unsat: + self.frames[i+1].add(l) + self.states[i+1].add(l.clause) + pushed |= {l} + self.frames[i] = self.frames[i] - pushed + if (not (self.states[i].R - self.states[i+1].R) + and len(self.states[i].R) != 0): + inv = prune(self.states[i].R) + F_inf = self.frames[i] + j = i + 1 + while j < len(self.states): + for l in F_inf: + self.states[j].add(l.clause) + j += 1 + self.frames[len(self.states)-1] = F_inf + self.frames[i] = set([]) + break + elif (len(self.states[i].R) == 0 + and len(self.states[i+1].R) == 0): + break + i += 1 + + if inv is not None: + self.s_bad.push() + self.s_bad.add(And(inv)) + is_sat = self.s_bad.check() + if is_sat == unsat: + self.s_bad.pop() + return And(inv) + self.s_bad.pop() + return None + + def run(self): + if not check_disjoint(self.init, self.bad): + return "goal is reached in initial state" + level = 0 + while True: + inv = self.is_valid() # self.add_solver() here + if inv is not None: + return inv + is_sat, cube = self.unfold(level) + if is_sat == unsat: + level += 1 + if verbose: + print("Unfold %d" % level) + sys.stdout.flush() + elif is_sat == sat: + cex = self.quip_blocked(cube, level) + if cex is not None: + return cex + else: + return is_sat + +def test(file): + h2t = Horn2Transitions() + h2t.parse(file) + if verbose: + print("Test file: %s") % file + mp = Quip(h2t.init, h2t.trans, h2t.goal, h2t.xs, h2t.inputs, h2t.xns) + start_time = time.time() + result = mp.run() + end_time = time.time() + if isinstance(result, QGoal): + g = result + if verbose: + print("Trace") + while g: + if verbose: + print(g.level, g.cube) + g = g.parent + print("--- used %.3f seconds ---" % (end_time - start_time)) + validate(mp, result, mp.trans) + return + if isinstance(result, ExprRef): + if verbose: + print("Invariant:\n%s " % result) + print("--- used %.3f seconds ---" % (end_time - start_time)) + validate(mp, result, mp.trans) + return + print(result) + +def validate(var, result, trans): + if isinstance(result, QGoal): + g = result + s = fd_solver() + s.add(trans) + while g.parent is not None: + s.push() + s.add(var.prev(g.cube)) + s.add(var.next(g.parent.cube)) + assert sat == s.check() + s.pop() + g = g.parent + if verbose: + print "--- validation succeed ----" + return + if isinstance(result, ExprRef): + inv = result + s = fd_solver() + s.add(trans) + s.push() + s.add(var.prev(inv)) + s.add(Not(var.next(inv))) + assert unsat == s.check() + s.pop() + cube = var.prev(var.init) + step = 0 + while True: + step += 1 + # too many steps to reach invariant + if step > 1000: + if verbose: + print "--- validation failed --" + return + if not check_disjoint(var.prev(cube), var.prev(inv)): + # reach invariant + break + s.push() + s.add(cube) + assert s.check() == sat + cube = var.projectN(s.model()) + s.pop() + if verbose: + print "--- validation succeed ----" + return + + + +test("data/horn1.smt2") +test("data/horn2.smt2") +test("data/horn3.smt2") +test("data/horn4.smt2") +test("data/horn5.smt2") +# test("data/horn6.smt2") # not able to finish diff --git a/scripts/mk_genfile_common.py b/scripts/mk_genfile_common.py index c42eaf86d..6354613fe 100644 --- a/scripts/mk_genfile_common.py +++ b/scripts/mk_genfile_common.py @@ -8,6 +8,7 @@ # You should **not** import ``mk_util`` here # to avoid having this code depend on the # of the Python build system. +import io import os import pprint import logging @@ -622,7 +623,7 @@ def mk_gparams_register_modules_internal(h_files_full_path, path): reg_mod_descr_pat = re.compile('[ \t]*REG_MODULE_DESCRIPTION\(\'([^\']*)\', *\'([^\']*)\'\)') for h_file in sorted_headers_by_component(h_files_full_path): added_include = False - with open(h_file, 'r') as fin: + with io.open(h_file, encoding='utf-8', mode='r') as fin: for line in fin: m = reg_pat.match(line) if m: @@ -696,7 +697,7 @@ def mk_install_tactic_cpp_internal(h_files_full_path, path): for h_file in sorted_headers_by_component(h_files_full_path): added_include = False try: - with open(h_file, 'r') as fin: + with io.open(h_file, encoding='utf-8', mode='r') as fin: for line in fin: if tactic_pat.match(line): if not added_include: @@ -719,7 +720,7 @@ def mk_install_tactic_cpp_internal(h_files_full_path, path): fullname, line)) raise e except Exception as e: - _loggeer.error("Failed to read file {}\n".format(h_file)) + _logger.error("Failed to read file {}\n".format(h_file)) raise e # First pass will just generate the tactic factories fout.write('#define ADD_TACTIC_CMD(NAME, DESCR, CODE) ctx.insert(alloc(tactic_cmd, symbol(NAME), DESCR, [](ast_manager &m, const params_ref &p) { return CODE; }))\n') @@ -764,7 +765,7 @@ def mk_mem_initializer_cpp_internal(h_files_full_path, path): finalizer_pat = re.compile('[ \t]*ADD_FINALIZER\(\'([^\']*)\'\)') for h_file in sorted_headers_by_component(h_files_full_path): added_include = False - with open(h_file, 'r') as fin: + with io.open(h_file, encoding='utf-8', mode='r') as fin: for line in fin: m = initializer_pat.match(line) if m: diff --git a/scripts/mk_nuget_release.py b/scripts/mk_nuget_release.py index 7ce14bf5d..db7a1af64 100644 --- a/scripts/mk_nuget_release.py +++ b/scripts/mk_nuget_release.py @@ -38,7 +38,7 @@ def download_installs(): urllib.request.urlretrieve(url, "packages/%s" % name) os_info = {"z64-ubuntu-14" : ('so', 'ubuntu.14.04-x64'), - 'ubuntu-16' : ('so', 'ubuntu.16.04-x64'), + 'ubuntu-16' : ('so', 'ubuntu-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), 'osx' : ('dylib', 'macos'), @@ -52,7 +52,7 @@ def classify_package(f): return None def unpack(): - shutil.rmtree("out") + shutil.rmtree("out", ignore_errors=True) # unzip files in packages # out # +- runtimes @@ -70,9 +70,9 @@ def unpack(): path = os.path.abspath(os.path.join("packages", f)) zip_ref = zipfile.ZipFile(path, 'r') zip_ref.extract("%s/bin/libz3.%s" % (package_dir, ext), "tmp") - mk_dir("out/runtimes/%s" % dst) - shutil.move("tmp/%s/bin/libz3.%s" % (package_dir, ext), "out/runtimes/%s/." % dst, "/y") - if "win" in f: + mk_dir("out/runtimes/%s/native" % dst) + shutil.move("tmp/%s/bin/libz3.%s" % (package_dir, ext), "out/runtimes/%s/native/." % dst, "/y") + if "x64-win" in f: mk_dir("out/lib/netstandard1.4/") for b in ["Microsoft.Z3.dll"]: zip_ref.extract("%s/bin/%s" % (package_dir, b), "tmp") @@ -85,17 +85,18 @@ def create_nuget_spec(): Microsoft.Z3 %s Microsoft - Z3 is a satisfiability modulo theories solver from Microsoft Research. + +Z3 is a satisfiability modulo theories solver from Microsoft Research. + +Linux Dependencies: + libgomp.so.1 installed + Copyright Microsoft Corporation. All rights reserved. smt constraint solver theorem prover https://raw.githubusercontent.com/Z3Prover/z3/master/package/icon.jpg https://github.com/Z3Prover/z3 https://raw.githubusercontent.com/Z3Prover/z3/master/LICENSE.txt - + true en diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 8cf60ab62..2748e30a1 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -8,7 +8,7 @@ from mk_util import * def init_version(): - set_version(4, 8, 4, 0) + set_version(4, 8, 5, 0) # Z3 Project definition def init_project_def(): @@ -87,7 +87,7 @@ def init_project_def(): export_files=API_files, staging_link='python') add_dot_net_dll('dotnet', ['api_dll'], 'api/dotnet', dll_name='Microsoft.Z3', assembly_info_dir='Properties', default_key_file='src/api/dotnet/Microsoft.Z3.snk') - add_dot_net_core_dll('dotnetcore', ['api_dll'], 'api/dotnet', dll_name='Microsoft.Z3', assembly_info_dir='Properties', default_key_file='src/api/dotnet/Microsoft.Z3.snk') + add_dot_net_core_dll('dotnetcore', ['api_dll'], 'api/dotnet', dll_name='Microsoft.Z3', default_key_file='src/api/dotnet/Microsoft.Z3.snk') add_java_dll('java', ['api_dll'], 'api/java', dll_name='libz3java', package_name="com.microsoft.z3", manifest_file='manifest') add_ml_lib('ml', ['api_dll'], 'api/ml', lib_name='libz3ml') add_hlib('cpp', 'api/c++', includes2install=['z3++.h']) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 83ae3d455..2e827a7f3 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -6,6 +6,7 @@ # # Author: Leonardo de Moura (leonardo) ############################################ +import io import sys import os import re @@ -90,6 +91,7 @@ TRACE = False PYTHON_ENABLED=False DOTNET_ENABLED=False DOTNET_CORE_ENABLED=False +ESRP_SIGN=False DOTNET_KEY_FILE=getenv("Z3_DOTNET_KEY_FILE", None) JAVA_ENABLED=False ML_ENABLED=False @@ -706,14 +708,14 @@ def display_help(exit_code): # Parse configuration option for mk_make script def parse_options(): global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE, VS_PAR, VS_PAR_NUM - global DOTNET_ENABLED, DOTNET_CORE_ENABLED, DOTNET_KEY_FILE, JAVA_ENABLED, ML_ENABLED, JS_ENABLED, STATIC_LIB, STATIC_BIN, PREFIX, GMP, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH, GIT_DESCRIBE, PYTHON_INSTALL_ENABLED, PYTHON_ENABLED + global DOTNET_ENABLED, DOTNET_CORE_ENABLED, DOTNET_KEY_FILE, JAVA_ENABLED, ML_ENABLED, JS_ENABLED, STATIC_LIB, STATIC_BIN, PREFIX, GMP, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH, GIT_DESCRIBE, PYTHON_INSTALL_ENABLED, PYTHON_ENABLED, ESRP_SIGN global LINUX_X64, SLOW_OPTIMIZE, USE_OMP, LOG_SYNC global GUARD_CF, ALWAYS_DYNAMIC_BASE try: options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:df:sxhmcvtnp:gj', ['build=', 'debug', 'silent', 'x64', 'help', 'makefiles', 'showcpp', 'vsproj', 'guardcf', - 'trace', 'dotnet', 'dotnetcore', 'dotnet-key=', 'staticlib', 'prefix=', 'gmp', 'java', 'parallel=', 'gprof', 'js', + 'trace', 'dotnet', 'dotnetcore', 'dotnet-key=', 'esrp', 'staticlib', 'prefix=', 'gmp', 'java', 'parallel=', 'gprof', 'js', 'githash=', 'git-describe', 'x86', 'ml', 'optimize', 'noomp', 'pypkgdir=', 'python', 'staticbin', 'log-sync']) except: print("ERROR: Invalid command line option") @@ -751,6 +753,8 @@ def parse_options(): DOTNET_CORE_ENABLED = True elif opt in ('--dotnet-key'): DOTNET_KEY_FILE = arg + elif opt in ('--esrp'): + ESRP_SIGN = True elif opt in ('--staticlib'): STATIC_LIB = True elif opt in ('--staticbin'): @@ -803,7 +807,7 @@ def extract_c_includes(fname): # We should generate and error for any occurrence of #include that does not match the previous pattern. non_std_inc_pat = re.compile(".*#include.*") - f = open(fname, 'r') + f = io.open(fname, encoding='utf-8', mode='r') linenum = 1 for line in f: m1 = std_inc_pat.match(line) @@ -1770,6 +1774,22 @@ class DotNetDLLComponent(Component): def has_assembly_info(self): return True + def make_assembly_info(c, major, minor, build, revision): + assembly_info_template = os.path.join(c.src_dir, c.assembly_info_dir, 'AssemblyInfo.cs.in') + assembly_info_output = assembly_info_template[:-3] + assert assembly_info_output.endswith('.cs') + if os.path.exists(assembly_info_template): + configure_file(assembly_info_template, assembly_info_output, + { 'VER_MAJOR': str(major), + 'VER_MINOR': str(minor), + 'VER_BUILD': str(build), + 'VER_REVISION': str(revision), + } + ) + else: + raise MKException("Failed to find assembly template info file '%s'" % assembly_info_template) + + def mk_win_dist(self, build_path, dist_path): if is_dotnet_enabled(): mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR)) @@ -1867,6 +1887,7 @@ class DotNetCoreDLLComponent(Component): key = "" if not self.key_file is None: key = "%s" % self.key_file + key += "\ntrue" if VS_X64: platform = 'x64' @@ -1921,18 +1942,87 @@ class DotNetCoreDLLComponent(Component): dotnetCmdLine.extend(['-o', path]) MakeRuleCmd.write_cmd(out, ' '.join(dotnetCmdLine)) - - out.write('\n') + self.sign_esrp(out) + out.write('\n') out.write('%s: %s\n\n' % (self.name, dllfile)) + def sign_esrp(self, out): + global ESRP_SIGNx + print("esrp-sign", ESRP_SIGN) + if not ESRP_SIGN: + return + + import uuid + guid = str(uuid.uuid4()) + path = os.path.abspath(BUILD_DIR).replace("\\","\\\\") + assemblySignStr = """ +{ + "Version": "1.0.0", + "SignBatches" + : + [ + { + "SourceLocationType": "UNC", + "SourceRootDirectory": "%s", + "DestinationLocationType": "UNC", + "DestinationRootDirectory": "c:\\\\ESRP\\\\output", + "SignRequestFiles": [ + { + "CustomerCorrelationId": "%s", + "SourceLocation": "libz3.dll", + "DestinationLocation": "libz3.dll" + }, + { + "CustomerCorrelationId": "%s", + "SourceLocation": "Microsoft.Z3.dll", + "DestinationLocation": "Microsoft.Z3.dll" + } + ], + "SigningInfo": { + "Operations": [ + { + "KeyCode" : "CP-230012", + "OperationCode" : "SigntoolSign", + "Parameters" : { + "OpusName": "Microsoft", + "OpusInfo": "http://www.microsoft.com", + "FileDigest": "/fd \\"SHA256\\"", + "PageHash": "/NPH", + "TimeStamp": "/tr \\"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\\" /td sha256" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-230012", + "OperationCode" : "SigntoolVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + } + } + ] +} """ % (path, guid, guid) + assemblySign = os.path.join(os.path.abspath(BUILD_DIR), 'dotnet', 'assembly-sign-input.json') + with open(assemblySign, 'w') as ous: + ous.write(assemblySignStr) + outputFile = os.path.join(os.path.abspath(BUILD_DIR), 'dotnet', "esrp-out.json") + esrpCmdLine = ["esrpclient.exe", "sign", "-a", "C:\\esrp\\config\\authorization.json", "-p", "C:\\esrp\\config\\policy.json", "-i", assemblySign, "-o", outputFile] + MakeRuleCmd.write_cmd(out, ' '.join(esrpCmdLine)) + MakeRuleCmd.write_cmd(out, "move /Y C:\\esrp\\output\\libz3.dll .") + MakeRuleCmd.write_cmd(out, "move /Y C:\\esrp\\output\\Microsoft.Z3.dll .") + def main_component(self): return is_dotnet_core_enabled() def has_assembly_info(self): # TBD: is this required for dotnet core given that version numbers are in z3.csproj file? - return True + return False + def mk_win_dist(self, build_path, dist_path): if is_dotnet_core_enabled(): mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR)) @@ -2635,7 +2725,7 @@ def mk_config(): 'SLINK_FLAGS=/nologo /LDd\n' % static_opt) if VS_X64: config.write( - 'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _AMD64_ /D _DEBUG /D Z3DEBUG /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze- %s %s\n' % (extra_opt, static_opt)) + 'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D Z3DEBUG /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze- %s %s\n' % (extra_opt, static_opt)) config.write( 'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT %s\n' 'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 %s %s\n' % (link_extra_opt, maybe_disable_dynamic_base, link_extra_opt)) @@ -2660,7 +2750,7 @@ def mk_config(): extra_opt = '%s /D _TRACE ' % extra_opt if VS_X64: config.write( - 'CXXFLAGS=/c%s /Zi /nologo /W3 /WX- /O2 /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG /D _LIB /D _WINDOWS /D _AMD64_ /D _UNICODE /D UNICODE /Gm- /EHsc /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /TP %s %s\n' % (GL, extra_opt, static_opt)) + 'CXXFLAGS=/c%s /Zi /nologo /W3 /WX- /O2 /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG /D _LIB /D _WINDOWS /D _UNICODE /D UNICODE /Gm- /EHsc /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /TP %s %s\n' % (GL, extra_opt, static_opt)) config.write( 'LINK_EXTRA_FLAGS=/link%s /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 %s\n' 'SLINK_EXTRA_FLAGS=/link%s /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 %s\n' % (LTCG, link_extra_opt, LTCG, link_extra_opt)) @@ -2782,7 +2872,6 @@ def mk_config(): if is64(): if not sysname.startswith('CYGWIN') and not sysname.startswith('MSYS') and not sysname.startswith('MINGW'): CXXFLAGS = '%s -fPIC' % CXXFLAGS - CPPFLAGS = '%s -D_AMD64_' % CPPFLAGS if sysname == 'Linux': CPPFLAGS = '%s -D_USE_THREAD_LOCAL' % CPPFLAGS elif not LINUX_X64: @@ -3020,19 +3109,7 @@ def mk_version_dot_h(major, minor, build, revision): def mk_all_assembly_infos(major, minor, build, revision): for c in get_components(): if c.has_assembly_info(): - assembly_info_template = os.path.join(c.src_dir, c.assembly_info_dir, 'AssemblyInfo.cs.in') - assembly_info_output = assembly_info_template[:-3] - assert assembly_info_output.endswith('.cs') - if os.path.exists(assembly_info_template): - configure_file(assembly_info_template, assembly_info_output, - { 'VER_MAJOR': str(major), - 'VER_MINOR': str(minor), - 'VER_BUILD': str(build), - 'VER_REVISION': str(revision), - } - ) - else: - raise MKException("Failed to find assembly template info file '%s'" % assembly_info_template) + c.make_assembly_info(major, minor, build, revision) def get_header_files_for_components(component_src_dirs): assert isinstance(component_src_dirs, list) diff --git a/scripts/mk_win_dist.py b/scripts/mk_win_dist.py index bd3cad18a..2a88c625c 100644 --- a/scripts/mk_win_dist.py +++ b/scripts/mk_win_dist.py @@ -26,6 +26,7 @@ DIST_DIR='dist' FORCE_MK=False DOTNET_ENABLED=True DOTNET_CORE_ENABLED=False +ESRP_SIGN=False DOTNET_KEY_FILE=None JAVA_ENABLED=True GIT_HASH=False @@ -65,6 +66,7 @@ def display_help(): print(" --nodotnet do not include .NET bindings in the binary distribution files.") print(" --dotnetcore build for dotnet core.") print(" --dotnet-key= sign the .NET assembly with the private key in .") + print(" --esrp sign with esrp.") print(" --nojava do not include Java bindings in the binary distribution files.") print(" --nopython do not include Python bindings in the binary distribution files.") print(" --githash include git hash in the Zip file.") @@ -74,7 +76,7 @@ def display_help(): # Parse configuration option for mk_make script def parse_options(): - global FORCE_MK, JAVA_ENABLED, GIT_HASH, DOTNET_ENABLED, DOTNET_CORE_ENABLED, DOTNET_KEY_FILE, PYTHON_ENABLED, X86ONLY, X64ONLY + global FORCE_MK, JAVA_ENABLED, GIT_HASH, DOTNET_ENABLED, DOTNET_CORE_ENABLED, DOTNET_KEY_FILE, PYTHON_ENABLED, X86ONLY, X64ONLY, ESRP_SIGN path = BUILD_DIR options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:hsf', ['build=', 'help', @@ -84,6 +86,7 @@ def parse_options(): 'nodotnet', 'dotnetcore', 'dotnet-key=', + 'esrp', 'githash', 'nopython', 'x86-only', @@ -109,6 +112,8 @@ def parse_options(): PYTHON_ENABLED = False elif opt == '--dotnet-key': DOTNET_KEY_FILE = arg + elif opt == '--esrp': + ESRP_SIGN = True elif opt == '--nojava': JAVA_ENABLED = False elif opt == '--githash': @@ -142,6 +147,8 @@ def mk_build_dir(path, x64): opts.append('--java') if x64: opts.append('-x') + if ESRP_SIGN: + opts.append('--esrp') if GIT_HASH: opts.append('--githash=%s' % mk_util.git_hash()) opts.append('--git-describe') @@ -210,6 +217,7 @@ def get_z3_name(x64): return 'z3-%s.%s.%s-%s-win' % (major, minor, build, platform) def mk_dist_dir(x64): + global ESRP_SIGN if x64: platform = "x64" build_path = BUILD_X64_DIR @@ -218,6 +226,7 @@ def mk_dist_dir(x64): build_path = BUILD_X86_DIR dist_path = os.path.join(DIST_DIR, get_z3_name(x64)) mk_dir(dist_path) + mk_util.ESRP_SIGN = ESRP_SIGN if DOTNET_CORE_ENABLED: mk_util.DOTNET_CORE_ENABLED = True else: diff --git a/scripts/update_api.py b/scripts/update_api.py index 901ea4fda..6b953eefe 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -338,26 +338,33 @@ def Z3_set_error_handler(ctx, hndlr, _elems=Elementaries(_lib.Z3_set_error_handl """) for sig in _API2PY: - name = sig[0] - result = sig[1] - params = sig[2] - num = len(params) - core_py.write("def %s(" % name) - display_args(num) - comma = ", " if num != 0 else "" - core_py.write("%s_elems=Elementaries(_lib.%s)):\n" % (comma, name)) - lval = "r = " if result != VOID else "" - core_py.write(" %s_elems.f(" % lval) - display_args_to_z3(params) - core_py.write(")\n") - if len(params) > 0 and param_type(params[0]) == CONTEXT and not name in Unwrapped: - core_py.write(" _elems.Check(a0)\n") - if result == STRING: - core_py.write(" return _to_pystr(r)\n") - elif result != VOID: - core_py.write(" return r\n") - core_py.write("\n") - core_py + mk_py_wrapper_single(sig) + if sig[1] == STRING: + mk_py_wrapper_single(sig, decode_string=False) + +def mk_py_wrapper_single(sig, decode_string=True): + name = sig[0] + result = sig[1] + params = sig[2] + num = len(params) + def_name = name + if not decode_string: + def_name += '_bytes' + core_py.write("def %s(" % def_name) + display_args(num) + comma = ", " if num != 0 else "" + core_py.write("%s_elems=Elementaries(_lib.%s)):\n" % (comma, name)) + lval = "r = " if result != VOID else "" + core_py.write(" %s_elems.f(" % lval) + display_args_to_z3(params) + core_py.write(")\n") + if len(params) > 0 and param_type(params[0]) == CONTEXT and not name in Unwrapped: + core_py.write(" _elems.Check(a0)\n") + if result == STRING and decode_string: + core_py.write(" return _to_pystr(r)\n") + elif result != VOID: + core_py.write(" return r\n") + core_py.write("\n") ## .NET API native interface @@ -584,7 +591,7 @@ def mk_java(java_dir, package_name): java_wrapper.write('extern "C" {\n') java_wrapper.write('#endif\n\n') java_wrapper.write('#ifdef __GNUC__\n#if __GNUC__ >= 4\n#define DLL_VIS __attribute__ ((visibility ("default")))\n#else\n#define DLL_VIS\n#endif\n#else\n#define DLL_VIS\n#endif\n\n') - java_wrapper.write('#if defined(_M_X64) || defined(_AMD64_)\n\n') + java_wrapper.write('#if defined(__LP64__) || defined(_WIN64)\n\n') java_wrapper.write('#define GETLONGAELEMS(T,OLD,NEW) \\\n') java_wrapper.write(' T * NEW = (OLD == 0) ? 0 : (T*) jenv->GetLongArrayElements(OLD, NULL);\n') java_wrapper.write('#define RELEASELONGAELEMS(OLD,NEW) \\\n') @@ -1335,6 +1342,10 @@ z3_long_funs = frozenset([ 'Z3_simplify_ex', ]) +z3_ml_overrides = frozenset([ + 'Z3_mk_config' + ]) + def mk_z3native_stubs_c(ml_src_dir, ml_output_dir): # C interface ml_wrapperf = os.path.join(ml_output_dir, 'z3native_stubs.c') ml_wrapper = open(ml_wrapperf, 'w') @@ -1346,6 +1357,10 @@ def mk_z3native_stubs_c(ml_src_dir, ml_output_dir): # C interface ml_pref.close() for name, result, params in _dotnet_decls: + + if name in z3_ml_overrides: + continue + ip = inparams(params) op = outparams(params) ap = arrayparams(params) @@ -1528,6 +1543,11 @@ def mk_z3native_stubs_c(ml_src_dir, ml_output_dir): # C interface i = i + 1 ml_wrapper.write(');\n') + if name in NULLWrapped: + ml_wrapper.write(' if (z3rv_m == NULL) {\n') + ml_wrapper.write(' caml_raise_with_string(*caml_named_value("Z3EXCEPTION"), "Object allocation failed");\n') + ml_wrapper.write(' }\n') + if release_caml_gc: ml_wrapper.write('\n caml_acquire_runtime_system();\n') diff --git a/src/ackermannization/ackr_bound_probe.cpp b/src/ackermannization/ackr_bound_probe.cpp index c6cdaf268..4c18a3f7e 100644 --- a/src/ackermannization/ackr_bound_probe.cpp +++ b/src/ackermannization/ackr_bound_probe.cpp @@ -65,7 +65,7 @@ public: for_each_expr_core(p, visited, g.form(i)); } const double total = ackr_helper::calculate_lemma_bound(p.m_fun2terms); - TRACE("ackr_bound_probe", tout << "total=" << total << std::endl;); + TRACE("ackermannize", tout << "total=" << total << std::endl;); return result(total); } diff --git a/src/ackermannization/ackr_model_converter.cpp b/src/ackermannization/ackr_model_converter.cpp index c5e04ab23..4815d5a2c 100644 --- a/src/ackermannization/ackr_model_converter.cpp +++ b/src/ackermannization/ackr_model_converter.cpp @@ -84,7 +84,7 @@ void ackr_model_converter::convert(model * source, model * destination) { } void ackr_model_converter::convert_constants(model * source, model * destination) { - TRACE("ackr_model", tout << "converting constants\n";); + TRACE("ackermannize", tout << "converting constants\n";); obj_map interpretations; model_evaluator evaluator(*source); evaluator.set_model_completion(true); @@ -113,7 +113,7 @@ void ackr_model_converter::convert_constants(model * source, model * destination void ackr_model_converter::add_entry(model_evaluator & evaluator, app* term, expr* value, obj_map& interpretations) { - TRACE("ackr_model", tout << "add_entry" + TRACE("ackermannize", tout << "add_entry" << mk_ismt2_pp(term, m, 2) << "->" << mk_ismt2_pp(value, m, 2) << "\n"; @@ -137,7 +137,7 @@ void ackr_model_converter::add_entry(model_evaluator & evaluator, args.push_back(std::move(arg_value)); } if (fi->get_entry(args.c_ptr()) == nullptr) { - TRACE("ackr_model", + TRACE("ackermannize", tout << mk_ismt2_pp(declaration, m) << " args: " << std::endl; for (unsigned i = 0; i < args.size(); i++) tout << mk_ismt2_pp(args.get(i), m) << std::endl; @@ -145,7 +145,7 @@ void ackr_model_converter::add_entry(model_evaluator & evaluator, fi->insert_new_entry(args.c_ptr(), value); } else { - TRACE("ackr_model", tout << "entry already present\n";); + TRACE("ackermannize", tout << "entry already present\n";); } } diff --git a/src/ackermannization/lackr.cpp b/src/ackermannization/lackr.cpp index 9130d628c..4fe7797d4 100644 --- a/src/ackermannization/lackr.cpp +++ b/src/ackermannization/lackr.cpp @@ -56,7 +56,7 @@ lbool lackr::operator() () { if (!init()) return l_undef; const lbool rv = m_eager ? eager() : lazy(); if (rv == l_true) m_sat->get_model(m_model); - CTRACE("lackr", rv == l_true, + CTRACE("ackermannize", rv == l_true, model_smt2_pp(tout << "abstr_model(\n", m_m, *(m_model.get()), 2); tout << ")\n"; ); return rv; } @@ -89,7 +89,7 @@ bool lackr::init() { // Introduce ackermann lemma for the two given terms. // bool lackr::ackr(app * const t1, app * const t2) { - TRACE("lackr", tout << "ackr " + TRACE("ackermannize", tout << "ackr " << mk_ismt2_pp(t1, m_m, 2) << " , " << mk_ismt2_pp(t2, m_m, 2) << "\n";); const unsigned sz = t1->get_num_args(); SASSERT(t2->get_num_args() == sz); @@ -99,7 +99,7 @@ bool lackr::ackr(app * const t1, app * const t2) { expr * const arg2 = t2->get_arg(i); if (m_m.are_equal(arg1, arg2)) continue; // quickly skip syntactically equal if (m_m.are_distinct(arg1, arg2)){ // quickly abort if there are two distinct (e.g. numerals) - TRACE("lackr", tout << "never eq\n";); + TRACE("ackermannize", tout << "never eq\n";); return false; } eqs.push_back(m_m.mk_eq(arg1, arg2)); @@ -107,22 +107,22 @@ bool lackr::ackr(app * const t1, app * const t2) { app * const a1 = m_info->get_abstr(t1); app * const a2 = m_info->get_abstr(t2); SASSERT(a1 && a2); - TRACE("lackr", tout << "abstr1 " << mk_ismt2_pp(a1, m_m, 2) << "\n";); - TRACE("lackr", tout << "abstr2 " << mk_ismt2_pp(a2, m_m, 2) << "\n";); + TRACE("ackermannize", tout << "abstr1 " << mk_ismt2_pp(a1, m_m, 2) << "\n";); + TRACE("ackermannize", tout << "abstr2 " << mk_ismt2_pp(a2, m_m, 2) << "\n";); expr_ref lhs(m_m); lhs = (eqs.size() == 1) ? eqs.get(0) : m_m.mk_and(eqs.size(), eqs.c_ptr()); - TRACE("lackr", tout << "ackr constr lhs" << mk_ismt2_pp(lhs, m_m, 2) << "\n";); + TRACE("ackermannize", tout << "ackr constr lhs" << mk_ismt2_pp(lhs, m_m, 2) << "\n";); expr_ref rhs(m_m.mk_eq(a1, a2),m_m); - TRACE("lackr", tout << "ackr constr rhs" << mk_ismt2_pp(rhs, m_m, 2) << "\n";); + TRACE("ackermannize", tout << "ackr constr rhs" << mk_ismt2_pp(rhs, m_m, 2) << "\n";); expr_ref cg(m_m.mk_implies(lhs, rhs), m_m); - TRACE("lackr", tout << "ackr constr" << mk_ismt2_pp(cg, m_m, 2) << "\n";); + TRACE("ackermannize", tout << "ackr constr" << mk_ismt2_pp(cg, m_m, 2) << "\n";); expr_ref cga(m_m); m_info->abstract(cg, cga); // constraint needs abstraction due to nested applications m_simp(cga); - TRACE("lackr", tout << "ackr constr abs:" << mk_ismt2_pp(cga, m_m, 2) << "\n";); + TRACE("ackermannize", tout << "ackr constr abs:" << mk_ismt2_pp(cga, m_m, 2) << "\n";); if (m_m.is_true(cga)) return false; m_st.m_ackrs_sz++; - m_ackrs.push_back(cga); + m_ackrs.push_back(std::move(cga)); return true; } @@ -130,11 +130,10 @@ bool lackr::ackr(app * const t1, app * const t2) { // Introduce the ackermann lemma for each pair of terms. // void lackr::eager_enc() { - TRACE("lackr", tout << "#funs: " << m_fun2terms.size() << std::endl;); - const fun2terms_map::iterator e = m_fun2terms.end(); - for (fun2terms_map::iterator i = m_fun2terms.begin(); i != e; ++i) { + TRACE("ackermannize", tout << "#funs: " << m_fun2terms.size() << std::endl;); + for (auto const& kv : m_fun2terms) { checkpoint(); - app_set * const ts = i->get_value(); + app_set * const ts = kv.get_value(); const app_set::iterator r = ts->end(); for (app_set::iterator j = ts->begin(); j != r; ++j) { app_set::iterator k = j; @@ -142,10 +141,11 @@ void lackr::eager_enc() { for (; k != r; ++k) { app * const t1 = *j; app * const t2 = *k; - SASSERT(t1->get_decl() == i->m_key); - SASSERT(t2->get_decl() == i->m_key); - if (t1 == t2) continue; - ackr(t1,t2); + SASSERT(t1->get_decl() == kv.m_key); + SASSERT(t2->get_decl() == kv.m_key); + if (t1 != t2) { + ackr(t1,t2); + } } } } @@ -153,18 +153,15 @@ void lackr::eager_enc() { void lackr::abstract() { - const fun2terms_map::iterator e = m_fun2terms.end(); - for (fun2terms_map::iterator i = m_fun2terms.begin(); i != e; ++i) { - func_decl* const fd = i->m_key; - app_set * const ts = i->get_value(); + for (auto const& kv : m_fun2terms) { + func_decl* const fd = kv.m_key; + app_set * const ts = kv.get_value(); sort* const s = fd->get_range(); - const app_set::iterator r = ts->end(); - for (app_set::iterator j = ts->begin(); j != r; ++j) { + for (app * t : (*ts)) { app * const fc = m_m.mk_fresh_const(fd->get_name().str().c_str(), s); - app * const t = *j; SASSERT(t->get_decl() == fd); m_info->set_abstr(t, fc); - TRACE("lackr", tout << "abstr term " + TRACE("ackermannize", tout << "abstr term " << mk_ismt2_pp(t, m_m, 2) << " -> " << mk_ismt2_pp(fc, m_m, 2) @@ -189,7 +186,7 @@ void lackr::add_term(app* a) { ts = alloc(app_set); m_fun2terms.insert(fd, ts); } - TRACE("lackr", tout << "term(" << mk_ismt2_pp(a, m_m, 2) << ")\n";); + TRACE("ackermannize", tout << "term(" << mk_ismt2_pp(a, m_m, 2) << ")\n";); ts->insert(a); } @@ -203,7 +200,7 @@ void lackr::push_abstraction() { lbool lackr::eager() { SASSERT(m_is_init); push_abstraction(); - TRACE("lackr", tout << "run sat 0\n"; ); + TRACE("ackermannize", tout << "run sat 0\n"; ); const lbool rv0 = m_sat->check_sat(0, nullptr); if (rv0 == l_false) return l_false; eager_enc(); @@ -211,7 +208,7 @@ lbool lackr::eager() { all = m_m.mk_and(m_ackrs.size(), m_ackrs.c_ptr()); m_simp(all); m_sat->assert_expr(all); - TRACE("lackr", tout << "run sat all\n"; ); + TRACE("ackermannize", tout << "run sat all\n"; ); return m_sat->check_sat(0, nullptr); } @@ -223,7 +220,7 @@ lbool lackr::lazy() { while (true) { m_st.m_it++; checkpoint(); - TRACE("lackr", tout << "lazy check: " << m_st.m_it << "\n";); + TRACE("ackermannize", tout << "lazy check: " << m_st.m_it << "\n";); const lbool r = m_sat->check_sat(0, nullptr); if (r == l_undef) return l_undef; // give up if (r == l_false) return l_false; // abstraction unsat @@ -264,16 +261,15 @@ bool lackr::collect_terms() { visited.mark(curr, true); stack.pop_back(); break; - case AST_APP: - { + case AST_APP: { app * const a = to_app(curr); if (for_each_expr_args(stack, visited, a->get_num_args(), a->get_args())) { visited.mark(curr, true); stack.pop_back(); add_term(a); } - } break; + } case AST_QUANTIFIER: return false; // quantifiers not supported default: diff --git a/src/ackermannization/lackr.h b/src/ackermannization/lackr.h index 98c1988f7..049fb8bb3 100644 --- a/src/ackermannization/lackr.h +++ b/src/ackermannization/lackr.h @@ -102,7 +102,7 @@ class lackr { // // Introduce congruence ackermann lemma for the two given terms. // - bool ackr(app * const t1, app * const t2); + bool ackr(app * t1, app * t2); // // Introduce the ackermann lemma for each pair of terms. diff --git a/src/ackermannization/lackr_model_constructor.cpp b/src/ackermannization/lackr_model_constructor.cpp index df0aac15e..f24a53cdb 100644 --- a/src/ackermannization/lackr_model_constructor.cpp +++ b/src/ackermannization/lackr_model_constructor.cpp @@ -237,7 +237,7 @@ struct lackr_model_constructor::imp { // handle functions if (m_ackr_helper.should_ackermannize(a)) { // handle uninterpreted app_ref key(m_m.mk_app(a->get_decl(), values.c_ptr()), m_m); - if (!make_value_uninterpreted_function(a, values, key.get(), result)) { + if (!make_value_uninterpreted_function(a, key.get(), result)) { return false; } } @@ -284,7 +284,6 @@ struct lackr_model_constructor::imp { } bool make_value_uninterpreted_function(app* a, - expr_ref_vector& values, app* key, expr_ref& result) { // get ackermann constant @@ -370,15 +369,12 @@ lackr_model_constructor::lackr_model_constructor(ast_manager& m, ackr_info_ref i {} lackr_model_constructor::~lackr_model_constructor() { - if (m_imp) dealloc(m_imp); + dealloc(m_imp); } bool lackr_model_constructor::check(model_ref& abstr_model) { m_conflicts.reset(); - if (m_imp) { - dealloc(m_imp); - m_imp = nullptr; - } + dealloc(m_imp); m_imp = alloc(lackr_model_constructor::imp, m_m, m_info, abstr_model, m_conflicts); const bool rv = m_imp->check(); m_state = rv ? CHECKED : CONFLICT; diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index d10da60db..eeb85687d 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -34,6 +34,7 @@ Revision History: #include "ast/rewriter/var_subst.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/recfun_replace.h" +#include "ast/rewriter/seq_rewriter.h" #include "ast/pp.h" #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" @@ -733,6 +734,7 @@ extern "C" { Z3_CATCH_RETURN(Z3_L_UNDEF); } + static Z3_ast simplify(Z3_context c, Z3_ast _a, Z3_params _p) { Z3_TRY; RESET_ERROR_CODE(); @@ -742,6 +744,7 @@ extern "C" { unsigned timeout = p.get_uint("timeout", mk_c(c)->get_timeout()); bool use_ctrl_c = p.get_bool("ctrl_c", false); th_rewriter m_rw(m, p); + m_rw.set_solver(alloc(api::seq_expr_solver, m, p)); expr_ref result(m); cancel_eh eh(m.limit()); api::context::set_interruptable si(*(mk_c(c)), eh); diff --git a/src/api/api_config_params.cpp b/src/api/api_config_params.cpp index 54b3c5795..60d5fa556 100644 --- a/src/api/api_config_params.cpp +++ b/src/api/api_config_params.cpp @@ -68,10 +68,17 @@ extern "C" { } Z3_config Z3_API Z3_mk_config(void) { - memory::initialize(UINT_MAX); - LOG_Z3_mk_config(); - Z3_config r = reinterpret_cast(alloc(context_params)); - RETURN_Z3(r); + try { + memory::initialize(UINT_MAX); + LOG_Z3_mk_config(); + Z3_config r = reinterpret_cast(alloc(context_params)); + RETURN_Z3(r); + } catch (z3_exception & ex) { + // The error handler is only available for contexts + // Just throw a warning. + warning_msg("%s", ex.msg()); + return nullptr; + } } void Z3_API Z3_del_config(Z3_config c) { diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 0b1bf4490..a2492cb1a 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -109,13 +109,10 @@ namespace api { context::~context() { m_last_obj = nullptr; - u_map::iterator it = m_allocated_objects.begin(); - while (it != m_allocated_objects.end()) { - api::object* val = it->m_value; - DEBUG_CODE(warning_msg("Uncollected memory: %d: %s", it->m_key, typeid(*val).name());); - m_allocated_objects.remove(it->m_key); + for (auto& kv : m_allocated_objects) { + api::object* val = kv.m_value; + DEBUG_CODE(warning_msg("Uncollected memory: %d: %s", kv.m_key, typeid(*val).name());); dealloc(val); - it = m_allocated_objects.begin(); } } @@ -489,9 +486,3 @@ extern "C" { } }; - -Z3_API ast_manager& Z3_get_manager(Z3_context c) { - return mk_c(c)->m(); -} - - diff --git a/src/api/api_context.h b/src/api/api_context.h index a6b8600d6..aacd4edd3 100644 --- a/src/api/api_context.h +++ b/src/api/api_context.h @@ -38,6 +38,9 @@ Revision History: #include "cmd_context/cmd_context.h" #include "api/api_polynomial.h" #include "util/hashtable.h" +#include "ast/rewriter/seq_rewriter.h" +#include "smt/smt_solver.h" +#include "solver/solver.h" namespace smtlib { class parser; @@ -49,6 +52,24 @@ namespace realclosure { namespace api { + class seq_expr_solver : public expr_solver { + ast_manager& m; + params_ref const& p; + solver_ref s; + public: + seq_expr_solver(ast_manager& m, params_ref const& p): m(m), p(p) {} + lbool check_sat(expr* e) { + if (!s) { + s = mk_smt_solver(m, p, symbol("ALL")); + } + s->push(); + s->assert_expr(e); + lbool r = s->check_sat(); + s->pop(1); + return r; + } + }; + class context : public tactic_manager { struct add_plugins { add_plugins(ast_manager & m); }; diff --git a/src/api/api_fpa.cpp b/src/api/api_fpa.cpp index 9728cbc00..8301ea604 100644 --- a/src/api/api_fpa.cpp +++ b/src/api/api_fpa.cpp @@ -242,8 +242,8 @@ extern "C" { RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); - expr * a = negative != 0 ? ctx->fpautil().mk_ninf(to_sort(s)) : - ctx->fpautil().mk_pinf(to_sort(s)); + expr * a = negative ? ctx->fpautil().mk_ninf(to_sort(s)) : + ctx->fpautil().mk_pinf(to_sort(s)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); @@ -259,8 +259,8 @@ extern "C" { RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); - expr * a = negative != 0 ? ctx->fpautil().mk_nzero(to_sort(s)) : - ctx->fpautil().mk_pzero(to_sort(s)); + expr * a = negative ? ctx->fpautil().mk_nzero(to_sort(s)) : + ctx->fpautil().mk_pzero(to_sort(s)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); @@ -351,7 +351,7 @@ extern "C" { ctx->fpautil().fm().set(tmp, ctx->fpautil().get_ebits(to_sort(ty)), ctx->fpautil().get_sbits(to_sort(ty)), - sgn != 0, exp, sig); + sgn, exp, sig); expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); @@ -371,7 +371,7 @@ extern "C" { ctx->fpautil().fm().set(tmp, ctx->fpautil().get_ebits(to_sort(ty)), ctx->fpautil().get_sbits(to_sort(ty)), - sgn != 0, exp, sig); + sgn, exp, sig); expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp index c7347f299..0937e668e 100644 --- a/src/api/api_model.cpp +++ b/src/api/api_model.cpp @@ -79,11 +79,7 @@ extern "C" { Z3_TRY; LOG_Z3_model_has_interp(c, m, a); CHECK_NON_NULL(m, 0); - if (to_model_ref(m)->has_interpretation(to_func_decl(a))) { - return true; - } else { - return false; - } + return to_model_ref(m)->has_interpretation(to_func_decl(a)); Z3_CATCH_RETURN(false); } @@ -165,7 +161,10 @@ extern "C" { CHECK_NON_NULL(m, false); CHECK_IS_EXPR(t, false); model * _m = to_model_ref(m); - expr_ref result(mk_c(c)->m()); + params_ref p; + ast_manager& mgr = mk_c(c)->m(); + _m->set_solver(alloc(api::seq_expr_solver, mgr, p)); + expr_ref result(mgr); model::scoped_model_completion _scm(*_m, model_completion); result = (*_m)(to_expr(t)); mk_c(c)->save_ast_trail(result.get()); @@ -472,7 +471,7 @@ extern "C" { model_smt2_pp(buffer, mk_c(c)->m(), *(to_model_ref(m)), 0); // Hack for removing the trailing '\n' result = buffer.str(); - if (result.size() != 0) + if (!result.empty()) result.resize(result.size()-1); } else { diff --git a/src/api/api_numeral.cpp b/src/api/api_numeral.cpp index cfa64e2c3..90d5998f3 100644 --- a/src/api/api_numeral.cpp +++ b/src/api/api_numeral.cpp @@ -198,19 +198,19 @@ extern "C" { mpf_rounding_mode rm; if (mk_c(c)->fpautil().is_rm_numeral(to_expr(a), rm)) { switch (rm) { - case OP_FPA_RM_NEAREST_TIES_TO_EVEN: + case MPF_ROUND_NEAREST_TEVEN: return mk_c(c)->mk_external_string("roundNearestTiesToEven"); break; - case OP_FPA_RM_NEAREST_TIES_TO_AWAY: + case MPF_ROUND_NEAREST_TAWAY: return mk_c(c)->mk_external_string("roundNearestTiesToAway"); break; - case OP_FPA_RM_TOWARD_POSITIVE: + case MPF_ROUND_TOWARD_POSITIVE: return mk_c(c)->mk_external_string("roundTowardPositive"); break; - case OP_FPA_RM_TOWARD_NEGATIVE: + case MPF_ROUND_TOWARD_NEGATIVE: return mk_c(c)->mk_external_string("roundTowardNegative"); break; - case OP_FPA_RM_TOWARD_ZERO: + case MPF_ROUND_TOWARD_ZERO: default: return mk_c(c)->mk_external_string("roundTowardZero"); break; diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index 0b56b788d..5d35b23b0 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -79,6 +79,16 @@ extern "C" { Z3_CATCH; } + void Z3_API Z3_optimize_assert_and_track(Z3_context c, Z3_optimize o, Z3_ast a, Z3_ast t) { + Z3_TRY; + LOG_Z3_optimize_assert_and_track(c, o, a, t); + RESET_ERROR_CODE(); + CHECK_FORMULA(a,); + CHECK_FORMULA(t,); + to_optimize_ptr(o)->add_hard_constraint(to_expr(a), to_expr(t)); + Z3_CATCH; + } + unsigned Z3_API Z3_optimize_assert_soft(Z3_context c, Z3_optimize o, Z3_ast a, Z3_string weight, Z3_symbol id) { Z3_TRY; LOG_Z3_optimize_assert_soft(c, o, a, weight, id); diff --git a/src/api/api_params.cpp b/src/api/api_params.cpp index 31a196f96..b2fa2e815 100644 --- a/src/api/api_params.cpp +++ b/src/api/api_params.cpp @@ -66,7 +66,7 @@ extern "C" { Z3_TRY; LOG_Z3_params_set_bool(c, p, k, v); RESET_ERROR_CODE(); - to_params(p)->m_params.set_bool(norm_param_name(to_symbol(k)).c_str(), v != 0); + to_params(p)->m_params.set_bool(norm_param_name(to_symbol(k)).c_str(), v); Z3_CATCH; } diff --git a/src/api/api_rcf.cpp b/src/api/api_rcf.cpp index bcaf0869f..840f6d3a8 100644 --- a/src/api/api_rcf.cpp +++ b/src/api/api_rcf.cpp @@ -274,7 +274,7 @@ extern "C" { RESET_ERROR_CODE(); reset_rcf_cancel(c); std::ostringstream buffer; - rcfm(c).display(buffer, to_rcnumeral(a), compact != 0, html != 0); + rcfm(c).display(buffer, to_rcnumeral(a), compact, html); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index 6b48360a3..19e298ee8 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -106,8 +106,8 @@ extern "C" { SET_ERROR_CODE(Z3_INVALID_ARG, "expression is not a string literal"); return ""; } - std::string result = str.encode(); - return mk_c(c)->mk_external_string(result); + std::string s = str.encode(); + return mk_c(c)->mk_external_string(s); Z3_CATCH_RETURN(""); } diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index a5ad7b525..cafbfb9ff 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -163,12 +163,45 @@ extern "C" { to_solver_ref(s)->set_model_converter(ctx->get_model_converter()); } + static void solver_from_dimacs_stream(Z3_context c, Z3_solver s, std::istream& is) { + init_solver(c, s); + ast_manager& m = to_solver_ref(s)->get_manager(); + std::stringstream err; + sat::solver solver(to_solver_ref(s)->get_params(), m.limit()); + if (!parse_dimacs(is, err, solver)) { + SET_ERROR_CODE(Z3_PARSER_ERROR, err.str().c_str()); + return; + } + sat2goal s2g; + ref mc; + atom2bool_var a2b(m); + for (unsigned v = 0; v < solver.num_vars(); ++v) { + a2b.insert(m.mk_const(symbol(v), m.mk_bool_sort()), v); + } + goal g(m); + s2g(solver, a2b, to_solver_ref(s)->get_params(), g, mc); + for (unsigned i = 0; i < g.size(); ++i) { + to_solver_ref(s)->assert_expr(g.form(i)); + } + } + + // DIMACS files start with "p cnf" and number of variables/clauses. + // This is not legal SMT syntax, so use the DIMACS parser. + static bool is_dimacs_string(Z3_string c_str) { + return c_str[0] == 'p' && c_str[1] == ' ' && c_str[2] == 'c'; + } + void Z3_API Z3_solver_from_string(Z3_context c, Z3_solver s, Z3_string c_str) { Z3_TRY; LOG_Z3_solver_from_string(c, s, c_str); std::string str(c_str); std::istringstream is(str); - solver_from_stream(c, s, is); + if (is_dimacs_string(c_str)) { + solver_from_dimacs_stream(c, s, is); + } + else { + solver_from_stream(c, s, is); + } Z3_CATCH; } @@ -181,25 +214,8 @@ extern "C" { if (!is) { SET_ERROR_CODE(Z3_FILE_ACCESS_ERROR, nullptr); } - else if (ext && std::string("dimacs") == ext) { - ast_manager& m = to_solver_ref(s)->get_manager(); - std::stringstream err; - sat::solver solver(to_solver_ref(s)->get_params(), m.limit()); - if (!parse_dimacs(is, err, solver)) { - SET_ERROR_CODE(Z3_PARSER_ERROR, err.str().c_str()); - return; - } - sat2goal s2g; - ref mc; - atom2bool_var a2b(m); - for (unsigned v = 0; v < solver.num_vars(); ++v) { - a2b.insert(m.mk_const(symbol(v), m.mk_bool_sort()), v); - } - goal g(m); - s2g(solver, a2b, to_solver_ref(s)->get_params(), g, mc); - for (unsigned i = 0; i < g.size(); ++i) { - to_solver_ref(s)->assert_expr(g.form(i)); - } + else if (ext && (std::string("dimacs") == ext || std::string("cnf") == ext)) { + solver_from_dimacs_stream(c, s, is); } else { solver_from_stream(c, s, is); @@ -369,7 +385,7 @@ extern "C" { 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 fmls = to_solver_ref(s)->get_units(mk_c(c)->m()); + expr_ref_vector fmls = to_solver_ref(s)->get_units(); for (expr* f : fmls) { v->m_ast_vector.push_back(f); } @@ -384,7 +400,7 @@ extern "C" { 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 fmls = to_solver_ref(s)->get_non_units(mk_c(c)->m()); + expr_ref_vector fmls = to_solver_ref(s)->get_non_units(); for (expr* f : fmls) { v->m_ast_vector.push_back(f); } @@ -392,6 +408,53 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + void Z3_API Z3_solver_get_levels(Z3_context c, Z3_solver s, Z3_ast_vector literals, unsigned sz, unsigned levels[]) { + Z3_TRY; + LOG_Z3_solver_get_levels(c, s, literals, sz, levels); + RESET_ERROR_CODE(); + init_solver(c, s); + if (sz != Z3_ast_vector_size(c, literals)) { + SET_ERROR_CODE(Z3_IOB, nullptr); + return; + } + ptr_vector _vars; + for (unsigned i = 0; i < sz; ++i) { + expr* e = to_expr(Z3_ast_vector_get(c, literals, i)); + mk_c(c)->m().is_not(e, e); + _vars.push_back(e); + } + unsigned_vector _levels(sz); + to_solver_ref(s)->get_levels(_vars, _levels); + for (unsigned i = 0; i < sz; ++i) { + levels[i] = _levels[i]; + } + Z3_CATCH; + } + + void Z3_API Z3_solver_set_activity(Z3_context c, Z3_solver s, Z3_ast a, double activity) { + Z3_TRY; + LOG_Z3_solver_set_activity(c, s, a, activity); + RESET_ERROR_CODE(); + init_solver(c, s); + to_solver_ref(s)->set_activity(to_expr(a), activity); + Z3_CATCH; + } + + Z3_ast_vector Z3_API Z3_solver_get_trail(Z3_context c, Z3_solver s) { + Z3_TRY; + LOG_Z3_solver_get_trail(c, s); + RESET_ERROR_CODE(); + 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 trail = to_solver_ref(s)->get_trail(); + for (expr* f : trail) { + v->m_ast_vector.push_back(f); + } + RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(nullptr); + } + static Z3_lbool _solver_check(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]) { for (unsigned i = 0; i < num_assumptions; i++) { if (!is_expr(to_ast(assumptions[i]))) { @@ -532,6 +595,17 @@ extern "C" { Z3_CATCH_RETURN(""); } + Z3_string Z3_API Z3_solver_to_dimacs_string(Z3_context c, Z3_solver s) { + Z3_TRY; + LOG_Z3_solver_to_string(c, s); + RESET_ERROR_CODE(); + init_solver(c, s); + std::ostringstream buffer; + to_solver_ref(s)->display_dimacs(buffer); + return mk_c(c)->mk_external_string(buffer.str()); + Z3_CATCH_RETURN(""); + } + Z3_lbool Z3_API Z3_get_implied_equalities(Z3_context c, Z3_solver s, diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index d5d3bc1ff..6113fdbe9 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -388,6 +388,7 @@ namespace z3 { template array(ast_vector_tpl const & v); ~array() { delete[] m_array; } + void resize(unsigned sz) { delete[] m_array; m_size = sz; m_array = new T[sz]; } unsigned size() const { return m_size; } T & operator[](int i) { assert(0 <= i); assert(static_cast(i) < m_size); return m_array[i]; } T const & operator[](int i) const { assert(0 <= i); assert(static_cast(i) < m_size); return m_array[i]; } @@ -505,7 +506,7 @@ namespace z3 { out << Z3_ast_to_string(n.ctx(), n.m_ast); return out; } - inline bool eq(ast const & a, ast const & b) { return Z3_is_eq_ast(a.ctx(), a, b) != 0; } + inline bool eq(ast const & a, ast const & b) { return Z3_is_eq_ast(a.ctx(), a, b); } /** @@ -518,6 +519,12 @@ namespace z3 { sort(context & c, Z3_ast a):ast(c, a) {} sort(sort const & s):ast(s) {} operator Z3_sort() const { return reinterpret_cast(m_ast); } + + /** + \brief retrieve unique identifier for func_decl. + */ + unsigned id() const { unsigned r = Z3_get_sort_id(ctx(), *this); check_error(); return r; } + /** \brief Return true if this sort and \c s are equal. */ @@ -615,6 +622,11 @@ namespace z3 { operator Z3_func_decl() const { return reinterpret_cast(m_ast); } func_decl & operator=(func_decl const & s) { return static_cast(ast::operator=(s)); } + /** + \brief retrieve unique identifier for func_decl. + */ + unsigned id() const { unsigned r = Z3_get_func_decl_id(ctx(), *this); check_error(); return r; } + unsigned arity() const { return Z3_get_arity(ctx(), *this); } sort domain(unsigned i) const { assert(i < arity()); Z3_sort r = Z3_get_domain(ctx(), *this, i); check_error(); return sort(ctx(), r); } sort range() const { Z3_sort r = Z3_get_range(ctx(), *this); check_error(); return sort(ctx(), r); } @@ -713,10 +725,10 @@ namespace z3 { small integers, 64 bit integers or rational or decimal strings. */ bool is_numeral() const { return kind() == Z3_NUMERAL_AST; } - bool is_numeral_i64(int64_t& i) const { bool r = 0 != Z3_get_numeral_int64(ctx(), m_ast, &i); check_error(); return r;} - bool is_numeral_u64(uint64_t& i) const { bool r = 0 != Z3_get_numeral_uint64(ctx(), m_ast, &i); check_error(); return r;} - bool is_numeral_i(int& i) const { bool r = 0 != Z3_get_numeral_int(ctx(), m_ast, &i); check_error(); return r;} - bool is_numeral_u(unsigned& i) const { bool r = 0 != Z3_get_numeral_uint(ctx(), m_ast, &i); check_error(); return r;} + bool is_numeral_i64(int64_t& i) const { bool r = Z3_get_numeral_int64(ctx(), m_ast, &i); check_error(); return r;} + bool is_numeral_u64(uint64_t& i) const { bool r = Z3_get_numeral_uint64(ctx(), m_ast, &i); check_error(); return r;} + bool is_numeral_i(int& i) const { bool r = Z3_get_numeral_int(ctx(), m_ast, &i); check_error(); return r;} + bool is_numeral_u(unsigned& i) const { bool r = Z3_get_numeral_uint(ctx(), m_ast, &i); check_error(); return r;} bool is_numeral(std::string& s) const { if (!is_numeral()) return false; s = Z3_get_numeral_string(ctx(), m_ast); check_error(); return true; } bool is_numeral(std::string& s, unsigned precision) const { if (!is_numeral()) return false; s = Z3_get_numeral_decimal_string(ctx(), m_ast, precision); check_error(); return true; } bool is_numeral(double& d) const { if (!is_numeral()) return false; d = Z3_get_numeral_double(ctx(), m_ast); check_error(); return true; } @@ -736,15 +748,15 @@ namespace z3 { /** \brief Return true if this expression is a universal quantifier. */ - bool is_forall() const { return 0 != Z3_is_quantifier_forall(ctx(), m_ast); } + bool is_forall() const { return Z3_is_quantifier_forall(ctx(), m_ast); } /** \brief Return true if this expression is an existential quantifier. */ - bool is_exists() const { return 0 != Z3_is_quantifier_exists(ctx(), m_ast); } + bool is_exists() const { return Z3_is_quantifier_exists(ctx(), m_ast); } /** \brief Return true if this expression is a lambda expression. */ - bool is_lambda() const { return 0 != Z3_is_lambda(ctx(), m_ast); } + bool is_lambda() const { return Z3_is_lambda(ctx(), m_ast); } /** \brief Return true if this expression is a variable. @@ -753,12 +765,12 @@ namespace z3 { /** \brief Return true if expression is an algebraic number. */ - bool is_algebraic() const { return 0 != Z3_is_algebraic_number(ctx(), m_ast); } + bool is_algebraic() const { return Z3_is_algebraic_number(ctx(), m_ast); } /** \brief Return true if this expression is well sorted (aka type correct). */ - bool is_well_sorted() const { bool r = Z3_is_well_sorted(ctx(), m_ast) != 0; check_error(); return r; } + bool is_well_sorted() const { bool r = Z3_is_well_sorted(ctx(), m_ast); check_error(); return r; } /** \brief Return string representation of numeral or algebraic number @@ -771,6 +783,11 @@ namespace z3 { return std::string(Z3_get_numeral_decimal_string(ctx(), m_ast, precision)); } + /** + \brief retrieve unique identifier for expression. + */ + unsigned id() const { unsigned r = Z3_get_ast_id(ctx(), m_ast); check_error(); return r; } + /** \brief Return int value of numeral, throw if result cannot fit in machine int @@ -1720,6 +1737,10 @@ namespace z3 { m_vector = s.m_vector; return *this; } + ast_vector_tpl& set(unsigned idx, ast& a) { + Z3_ast_vector_set(ctx(), m_vector, idx, a); + return *this; + } /* Disabled pending C++98 build upgrade bool contains(T const& x) const { @@ -1746,6 +1767,9 @@ namespace z3 { ++m_index; return *this; } + void set(T& arg) { + Z3_ast_vector_set(m_vector->ctx(), *m_vector, m_index, arg); + } iterator operator++(int) { iterator tmp = *this; ++m_index; return tmp; } T * operator->() const { return &(operator*()); } T operator*() const { return (*m_vector)[m_index]; } @@ -2073,7 +2097,7 @@ namespace z3 { // for function f. bool has_interp(func_decl f) const { check_context(*this, f); - return 0 != Z3_model_has_interp(ctx(), m_model, f); + return Z3_model_has_interp(ctx(), m_model, f); } func_interp add_func_interp(func_decl& f, expr& else_val) { @@ -2112,8 +2136,8 @@ namespace z3 { } unsigned size() const { return Z3_stats_size(ctx(), m_stats); } std::string key(unsigned i) const { Z3_string s = Z3_stats_get_key(ctx(), m_stats, i); check_error(); return s; } - bool is_uint(unsigned i) const { bool r = Z3_stats_is_uint(ctx(), m_stats, i); check_error(); return r != 0; } - bool is_double(unsigned i) const { bool r = Z3_stats_is_double(ctx(), m_stats, i); check_error(); return r != 0; } + bool is_uint(unsigned i) const { bool r = Z3_stats_is_uint(ctx(), m_stats, i); check_error(); return r; } + bool is_double(unsigned i) const { bool r = Z3_stats_is_double(ctx(), m_stats, i); check_error(); return r; } unsigned uint_value(unsigned i) const { unsigned r = Z3_stats_get_uint_value(ctx(), m_stats, i); check_error(); return r; } double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } friend std::ostream & operator<<(std::ostream & out, stats const & s); @@ -2210,6 +2234,18 @@ namespace z3 { expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } expr_vector non_units() const { Z3_ast_vector r = Z3_solver_get_non_units(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } expr_vector units() const { Z3_ast_vector r = Z3_solver_get_units(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } + expr_vector trail() const { Z3_ast_vector r = Z3_solver_get_trail(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } + expr_vector trail(array& levels) const { + Z3_ast_vector r = Z3_solver_get_trail(ctx(), m_solver); + check_error(); + expr_vector result(ctx(), r); + unsigned sz = result.size(); + levels.resize(sz); + Z3_solver_get_levels(ctx(), m_solver, r, sz, levels.ptr()); + check_error(); + return result; + } + void set_activity(expr const& lit, double act) { Z3_solver_set_activity(ctx(), m_solver, lit, act); } expr proof() const { Z3_ast r = Z3_solver_get_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } friend std::ostream & operator<<(std::ostream & out, solver const & s); @@ -2233,6 +2269,8 @@ namespace z3 { fml)); } + std::string dimacs() const { return std::string(Z3_solver_to_dimacs_string(ctx(), m_solver)); } + param_descrs get_param_descrs() { return param_descrs(ctx(), Z3_solver_get_param_descrs(ctx(), m_solver)); } @@ -2353,12 +2391,12 @@ namespace z3 { unsigned size() const { return Z3_goal_size(ctx(), m_goal); } expr operator[](int i) const { assert(0 <= i); Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } Z3_goal_prec precision() const { return Z3_goal_precision(ctx(), m_goal); } - bool inconsistent() const { return Z3_goal_inconsistent(ctx(), m_goal) != 0; } + bool inconsistent() const { return Z3_goal_inconsistent(ctx(), m_goal); } unsigned depth() const { return Z3_goal_depth(ctx(), m_goal); } void reset() { Z3_goal_reset(ctx(), m_goal); } unsigned num_exprs() const { return Z3_goal_num_exprs(ctx(), m_goal); } - bool is_decided_sat() const { return Z3_goal_is_decided_sat(ctx(), m_goal) != 0; } - bool is_decided_unsat() const { return Z3_goal_is_decided_unsat(ctx(), m_goal) != 0; } + bool is_decided_sat() const { return Z3_goal_is_decided_sat(ctx(), m_goal); } + bool is_decided_unsat() const { return Z3_goal_is_decided_unsat(ctx(), m_goal); } model convert_model(model const & m) const { check_context(*this, m); Z3_model new_m = Z3_goal_convert_model(ctx(), m_goal, m); @@ -2609,6 +2647,11 @@ namespace z3 { strm << weight; return handle(Z3_optimize_assert_soft(ctx(), m_opt, e, strm.str().c_str(), 0)); } + void add(expr const& e, expr const& t) { + assert(e.is_bool()); + Z3_optimize_assert_and_track(ctx(), m_opt, e, t); + } + handle add(expr const& e, char const* weight) { assert(e.is_bool()); return handle(Z3_optimize_assert_soft(ctx(), m_opt, e, weight, 0)); diff --git a/src/api/dotnet/CMakeLists.txt b/src/api/dotnet/CMakeLists.txt index 20621e4fc..3ff1a484a 100644 --- a/src/api/dotnet/CMakeLists.txt +++ b/src/api/dotnet/CMakeLists.txt @@ -1,12 +1,10 @@ -find_package(DotNetToolchain REQUIRED) +find_package(Dotnet REQUIRED) # Configure AssemblyInfo.cs set(VER_MAJOR "${Z3_VERSION_MAJOR}") set(VER_MINOR "${Z3_VERSION_MINOR}") set(VER_BUILD "${Z3_VERSION_PATCH}") set(VER_REVISION "${Z3_VERSION_TWEAK}") -set(Z3_DOTNET_ASSEMBLY_INFO_FILE "${CMAKE_CURRENT_BINARY_DIR}/Properties/AssemblyInfo.cs") -configure_file("Properties/AssemblyInfo.cs.in" "${Z3_DOTNET_ASSEMBLY_INFO_FILE}" @ONLY) # Generate Native.cs set(Z3_DOTNET_NATIVE_FILE "${CMAKE_CURRENT_BINARY_DIR}/Native.cs") @@ -127,160 +125,64 @@ endforeach() list(APPEND Z3_DOTNET_ASSEMBLY_SOURCES "${Z3_DOTNET_CONST_FILE}" "${Z3_DOTNET_NATIVE_FILE}" - "${Z3_DOTNET_ASSEMBLY_INFO_FILE}" ) -# ``csc.exe`` doesn't like UNIX style paths so convert them -# if necessary first to native paths. -set(Z3_DOTNET_ASSEMBLY_SOURCES_NATIVE_PATH "") -foreach (csfile_path ${Z3_DOTNET_ASSEMBLY_SOURCES}) - file(TO_NATIVE_PATH "${csfile_path}" csfile_path_native) - list(APPEND Z3_DOTNET_ASSEMBLY_SOURCES_NATIVE_PATH "${csfile_path_native}") + +# Generate items +set(Z3_DOTNET_COMPILE_ITEMS "") +foreach(csfile ${Z3_DOTNET_ASSEMBLY_SOURCES}) + set(Z3_DOTNET_COMPILE_ITEMS "${Z3_DOTNET_COMPILE_ITEMS}\n ") endforeach() -set(CSC_FLAGS "") -if (DOTNET_TOOLCHAIN_IS_WINDOWS) - # FIXME: Why use these flags? - # Note these flags have been copied from the Python build system. - list(APPEND CSC_FLAGS - "/noconfig" - "/nostdlib+" - "/reference:mscorlib.dll" - ) -elseif (DOTNET_TOOLCHAIN_IS_MONO) - # We need to give the assembly a strong name so that it can be installed - # into the GAC. - list(APPEND CSC_FLAGS - "/keyfile:${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Z3.snk" - ) + +# FindDotnet.cmake forwards CMake build type to MSBuild. +# And thus we can put the conditional properties in the project file. +# Note, nuget package file names do not have the ${VER_REV} part. + +set(Z3_DOTNET_NUPKG_VERSION "${VER_MAJOR}.${VER_MINOR}.${VER_BUILD}") +if("${TARGET_ARCHITECTURE}" STREQUAL "i686") + set(Z3_DOTNET_PLATFORM "x86") else() - message(FATAL_ERROR "Unknown .NET toolchain") + set(Z3_DOTNET_PLATFORM "AnyCPU") endif() -# Common flags -list(APPEND CSC_FLAGS - "/unsafe+" - "/nowarn:1701,1702" - "/errorreport:prompt" - "/warn:4" - "/reference:System.Core.dll" - "/reference:System.dll" - "/reference:System.Numerics.dll" - "/filealign:512" # Why? - "/target:library" -) +# TODO conditional for signing. we can then enable the ``Release_delaysign`` configuration -# Set the build type flags. The build type for the assembly roughly corresponds -# with the native code build type. -list(APPEND CSC_FLAGS - # Debug flags, expands to nothing if we aren't doing a debug build - "$<$:/debug+>" - "$<$:/debug:full>" - "$<$:/optimize->" - # This has to be quoted otherwise the ``;`` is interpreted as a command separator - "$<$:\"/define:DEBUG$TRACE\">" - # Release flags, expands to nothing if we are doing a debug build - "$<$>:/optimize+>" -) - -# Mono's gacutil crashes when trying to install an assembly if we set the -# platform in some cases, so only set it on Windows. This bug has been -# reported at https://bugzilla.xamarin.com/show_bug.cgi?id=39955 . However mono -# ignores the platform of an assembly when running it ( -# http://lists.ximian.com/pipermail/mono-devel-list/2015-November/043370.html ) -# so this shouldn't matter in practice. -if (DOTNET_TOOLCHAIN_IS_WINDOWS) - # Set platform for assembly - if ("${TARGET_ARCHITECTURE}" STREQUAL "x86_64") - list(APPEND CSC_FLAGS "/platform:x64") - elseif ("${TARGET_ARCHITECTURE}" STREQUAL "i686") - list(APPEND CSC_FLAGS "/platform:x86") - endif() -endif() - -# FIXME: Ideally we should emit files into a configuration specific directory -# when using multi-configuration generators so that the files generated by each -# configuration don't clobber each other. Unfortunately the ``get_property()`` -# command only works correctly for single configuration generators so we can't -# use it. We also can't use ``$`` because the ``OUTPUT`` -# argument to ``add_custom_commands()`` won't accept it. -# See http://public.kitware.com/pipermail/cmake/2016-March/063101.html -# -# For now just output file to the root binary directory like the Python build -# system does and emit a warning when appropriate. -if (DEFINED CMAKE_CONFIGURATION_TYPES) - # Multi-configuration build (e.g. Visual Studio and Xcode). - message(WARNING "You are using a multi-configuration generator. The build rules for" - " the \".NET\" bindings currently do not emit files per configuration so previously" - " generated files for other configurations will be overwritten.") -endif() - -set(Z3_DOTNET_ASSEMBLY_OUTPUT_DIR "${CMAKE_BINARY_DIR}") -set(Z3_DOTNET_ASSEMBLY_NAME "Microsoft.Z3.dll") -set(Z3_DOTNET_ASSEMBLY_DLL "${Z3_DOTNET_ASSEMBLY_OUTPUT_DIR}/${Z3_DOTNET_ASSEMBLY_NAME}") -# csc.exe doesn't work with UNIX style paths so convert to native path -file(TO_NATIVE_PATH "${Z3_DOTNET_ASSEMBLY_DLL}" Z3_DOTNET_ASSEMBLY_DLL_NATIVE_PATH) -set(Z3_DOTNET_ASSEMBLY_DLL_DOC "${Z3_DOTNET_ASSEMBLY_OUTPUT_DIR}/Microsoft.Z3.xml") -file(TO_NATIVE_PATH "${Z3_DOTNET_ASSEMBLY_DLL_DOC}" Z3_DOTNET_ASSEMBLY_DLL_DOC_NATIVE_PATH) -add_custom_command(OUTPUT "${Z3_DOTNET_ASSEMBLY_DLL}" "${Z3_DOTNET_ASSEMBLY_DLL_DOC}" - COMMAND - "${DOTNET_CSC_EXECUTABLE}" - ${CSC_FLAGS} - "/out:${Z3_DOTNET_ASSEMBLY_DLL_NATIVE_PATH}" - "/doc:${Z3_DOTNET_ASSEMBLY_DLL_DOC_NATIVE_PATH}" - ${Z3_DOTNET_ASSEMBLY_SOURCES_NATIVE_PATH} - DEPENDS - ${Z3_DOTNET_ASSEMBLY_SOURCES} - libz3 - WORKING_DIRECTORY "${Z3_DOTNET_ASSEMBLY_OUTPUT_DIR}" - COMMENT "Building \"${Z3_DOTNET_ASSEMBLY_DLL}\"" -) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Z3.csproj.in ${CMAKE_CURRENT_BINARY_DIR}/build/Microsoft.Z3.csproj) +ADD_DOTNET(${CMAKE_CURRENT_BINARY_DIR}/build/Microsoft.Z3.csproj + VERSION ${Z3_DOTNET_NUPKG_VERSION} + PLATFORM ${Z3_DOTNET_PLATFORM} + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Z3.csproj.in + ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Z3.props + ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Z3.targets + ${Z3_DOTNET_ASSEMBLY_SOURCES} + PACKAGE Microsoft.Z3 + PACK_ARGUMENTS "/p:_DN_CMAKE_CONFIG=$" + ) +add_dependencies(BUILD_Microsoft.Z3 libz3) # Convenient top-level target -add_custom_target(build_z3_dotnet_bindings - ALL - DEPENDS - "${Z3_DOTNET_ASSEMBLY_DLL}" -) +add_custom_target(build_z3_dotnet_bindings ALL DEPENDS BUILD_Microsoft.Z3) + +# Register the local nupkg repo +set(Z3_DOTNET_LOCALREPO_NAME "Microsoft Z3 Local Repository") +DOTNET_REGISTER_LOCAL_REPOSITORY(${Z3_DOTNET_LOCALREPO_NAME} ${CMAKE_BINARY_DIR}) ############################################################################### -# Install +# Install: register a local nuget repo and install our package. +# the build step depends on the 'purge' target, making sure that +# a user will always restore the freshly-built package. ############################################################################### option(INSTALL_DOTNET_BINDINGS "Install .NET bindings when invoking install target" ON) -set(GAC_PKG_NAME "Microsoft.Z3.Sharp") -set(PREFIX "${CMAKE_INSTALL_PREFIX}") -set(VERSION "${Z3_VERSION}") -set(Z3_DOTNET_PKGCONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/Microsoft.Z3.Sharp.pc") -configure_file("Microsoft.Z3.Sharp.pc.in" "${Z3_DOTNET_PKGCONFIG_FILE}" @ONLY) -if (DOTNET_TOOLCHAIN_IS_MONO) - message(STATUS "Emitting install rules for .NET bindings") - # Install pkgconfig file for the assembly. This is needed by Monodevelop - # to find the assembly - install(FILES "${Z3_DOTNET_PKGCONFIG_FILE}" DESTINATION "${CMAKE_INSTALL_PKGCONFIGDIR}") - - # Configure the install and uninstall scripts. - # Note: If multi-configuration generator support is ever fixed then these - # scripts will be broken. - configure_file(cmake_install_gac.cmake.in cmake_install_gac.cmake @ONLY) - configure_file(cmake_uninstall_gac.cmake.in cmake_uninstall_gac.cmake @ONLY) - - # Tell CMake to Invoke a script to install assembly to the GAC during install - install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/cmake_install_gac.cmake") - - # Add custom target to uninstall the assembly from the GAC - add_custom_target(remove_dotnet_dll_from_gac - COMMAND "${CMAKE_COMMAND}" "-P" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall_gac.cmake" - COMMENT "Uninstalling ${Z3_DOTNET_ASSEMBLY_NAME} from the GAC" - ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} - ) - add_dependencies(uninstall remove_dotnet_dll_from_gac) - -elseif(DOTNET_TOOLCHAIN_IS_WINDOWS) - # Don't install Z3_DOTNET_ASSEMBLY_DLL into the gac. Instead just copy into - # installation directory. - install(FILES "${Z3_DOTNET_ASSEMBLY_DLL}" DESTINATION "${CMAKE_INSTALL_LIBDIR}") - install(FILES "${Z3_DOTNET_ASSEMBLY_DLL_DOC}" DESTINATION "${CMAKE_INSTALL_LIBDIR}") -else() - message(FATAL_ERROR "Unknown .NET toolchain") +if(INSTALL_DOTNET_BINDINGS) + install(FILES "${CMAKE_BINARY_DIR}/Microsoft.Z3/Microsoft.Z3.${Z3_DOTNET_NUPKG_VERSION}.nupkg" DESTINATION "${CMAKE_INSTALL_LIBDIR}/z3.nuget") + # move the local repo to the installation directory (cancel the build-time repo) + install(CODE "include(${CMAKE_CURRENT_LIST_DIR}/../../../cmake/modules/FindDotnet.cmake)\n DOTNET_REGISTER_LOCAL_REPOSITORY(\"${Z3_DOTNET_LOCALREPO_NAME}\" ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/z3.nuget)") + install(FILES "${CMAKE_BINARY_DIR}/Microsoft.Z3/Microsoft.Z3.xml" DESTINATION "${CMAKE_INSTALL_LIBDIR}/z3.nuget") +# TODO GAC? +# set(GAC_PKG_NAME "Microsoft.Z3.Sharp") +# set(PREFIX "${CMAKE_INSTALL_PREFIX}") +# set(VERSION "${Z3_VERSION}") endif() + diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index a941f8e86..cdaae332b 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -475,7 +475,7 @@ namespace Microsoft.Z3 /// Update a datatype field at expression t with value v. /// The function performs a record update at t. The field /// that is passed in as argument is updated with value v, - /// the remainig fields of t are unchanged. + /// the remaining fields of t are unchanged. /// public Expr MkUpdateField(FuncDecl field, Expr t, Expr v) { diff --git a/src/api/dotnet/Goal.cs b/src/api/dotnet/Goal.cs index 4dbc78b7e..883b91f35 100644 --- a/src/api/dotnet/Goal.cs +++ b/src/api/dotnet/Goal.cs @@ -144,7 +144,7 @@ namespace Microsoft.Z3 uint n = Size; BoolExpr[] res = new BoolExpr[n]; for (uint i = 0; i < n; i++) - res[i] = new BoolExpr(Context, Native.Z3_goal_formula(Context.nCtx, NativeObject, i)); + res[i] = (BoolExpr)Expr.Create(Context, Native.Z3_goal_formula(Context.nCtx, NativeObject, i)); return res; } } diff --git a/src/api/dotnet/Lambda.cs b/src/api/dotnet/Lambda.cs index 35497f88f..1cd9db2dd 100644 --- a/src/api/dotnet/Lambda.cs +++ b/src/api/dotnet/Lambda.cs @@ -76,7 +76,7 @@ namespace Microsoft.Z3 get { - return new BoolExpr(Context, Native.Z3_get_quantifier_body(Context.nCtx, NativeObject)); + return Expr.Create(Context, Native.Z3_get_quantifier_body(Context.nCtx, NativeObject)); } } diff --git a/src/api/dotnet/Microsoft.Z3.csproj b/src/api/dotnet/Microsoft.Z3.csproj deleted file mode 100644 index 045c610dd..000000000 --- a/src/api/dotnet/Microsoft.Z3.csproj +++ /dev/null @@ -1,418 +0,0 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {EC3DB697-B734-42F7-9468-5B62821EEB5A} - Library - Properties - Microsoft.Z3 - Microsoft.Z3 - v4.0 - 512 - Client - 0 - - - true - full - false - ..\Debug\ - DEBUG;TRACE - prompt - 4 - true - ..\Debug\Microsoft.Z3.XML - False - False - True - False - False - True - False - True - True - False - False - False - True - False - False - False - True - False - False - True - True - True - False - False - - - - - - - True - Full - %28none%29 - 2 - - - pdbonly - true - ..\external\ - - - prompt - 4 - true - ..\external\Microsoft.Z3.xml - AnyCPU - - - ..\external\ - true - ..\external\Microsoft.Z3.xml - true - pdbonly - AnyCPU - bin\Release\Microsoft.Z3.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - true - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - true - false - - - true - ..\x64\Debug\ - DEBUG;TRACE - true - full - x64 - ..\Debug\Microsoft.Z3.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - true - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - true - True - False - True - False - False - False - False - False - False - False - False - False - False - False - False - False - True - False - False - True - False - False - False - - - - - - - False - Full - %28none%29 - 0 - ..\x64\Debug\Microsoft.Z3.XML - - - ..\x64\external_64\ - true - ..\x64\external_64\Microsoft.Z3.xml - true - pdbonly - x64 - ..\release\Microsoft.Z3.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - true - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - True - False - True - False - False - True - True - True - False - False - False - True - True - False - False - False - True - False - False - True - True - False - False - - - - - -repro - - True - Full - %28none%29 - 2 - - - ..\x64\external\ - true - ..\x64\external\Microsoft.Z3.XML - true - pdbonly - x64 - bin\Release\Microsoft.Z3.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - true - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - true - - - false - - - - - - - false - - - ..\Release_delaysign\ - true - ..\Release_delaysign\Microsoft.Z3.XML - true - pdbonly - AnyCPU - ..\release\Microsoft.Z3.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - true - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - true - DELAYSIGN - - - bin\x64\Release_delaysign\ - true - bin\x64\Release_delaysign\Microsoft.Z3.XML - true - pdbonly - x64 - ..\release\Microsoft.Z3.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - true - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - true - - - true - ..\x86\Debug\ - DEBUG;TRACE - true - full - x86 - ..\Debug\Microsoft.Z3.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - ..\x86\Debug\Microsoft.Z3.XML - - - bin\x86\Release\ - true - bin\x86\Release\Microsoft.Z3.xml - true - pdbonly - x86 - ..\external\Microsoft.Z3.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - - - bin\x86\external\ - true - bin\x86\external\Microsoft.Z3.XML - true - pdbonly - x86 - bin\Release\Microsoft.Z3.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - true - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - true - - - bin\x86\Release_delaysign\ - DELAYSIGN - true - bin\x86\Release_delaysign\Microsoft.Z3.XML - true - pdbonly - x86 - ..\release\Microsoft.Z3.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - true - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/api/dotnet/Microsoft.Z3.csproj.in b/src/api/dotnet/Microsoft.Z3.csproj.in new file mode 100644 index 000000000..fc6d6a978 --- /dev/null +++ b/src/api/dotnet/Microsoft.Z3.csproj.in @@ -0,0 +1,95 @@ + + + + + + Microsoft.Z3 + Microsoft.Z3 + Microsoft.Z3 + + Z3 .NET Interface + Z3 .NET Interface + + Z3 + + Z3 is a satisfiability modulo theories solver from Microsoft Research. + .NET Interface to the Z3 Theorem Prover + + Copyright (C) 2006-2019 Microsoft Corporation + Copyright (C) 2006-2019 Microsoft Corporation + + Microsoft Corporation + Microsoft Corporation + + @VER_MAJOR@.@VER_MINOR@.@VER_BUILD@.@VER_REVISION@ + @VER_MAJOR@.@VER_MINOR@.@VER_BUILD@.@VER_REVISION@ + + @VER_MAJOR@.@VER_MINOR@.@VER_BUILD@.@VER_REVISION@ + @VER_MAJOR@.@VER_MINOR@.@VER_BUILD@.@VER_REVISION@ + + ${DOTNET_PACKAGE_VERSION} + smt constraint solver theorem prover + + Microsoft + Microsoft + + + + + false + false + + + + DELAYSIGN + true + true + + + + + + netstandard2.0;net45 + library + True + 1701,1702 + 4 + true + $(OutputPath)\Microsoft.Z3.xml + + + + +${Z3_DOTNET_COMPILE_ITEMS} + + + + + + build + + + build + + + + + + + + + runtimes\win-x64\native + + + runtimes\linux-x64\native + + + + + + + runtimes\win-x86\native + + + + diff --git a/src/api/dotnet/Microsoft.Z3.props b/src/api/dotnet/Microsoft.Z3.props new file mode 100644 index 000000000..a5db71359 --- /dev/null +++ b/src/api/dotnet/Microsoft.Z3.props @@ -0,0 +1,23 @@ + + + + + + true + true + true + + + $(MSBuildThisFileDirectory)..\ + $(Z3_PACKAGE_PATH)runtimes\win-x64\native\libz3.dll + $(Z3_PACKAGE_PATH)runtimes\win-x86\native\libz3.dll + $(Z3_PACKAGE_PATH)runtimes\linux-x64\native\libz3.so + + + + + + false + + + diff --git a/src/api/dotnet/Microsoft.Z3.targets b/src/api/dotnet/Microsoft.Z3.targets new file mode 100644 index 000000000..38e56b350 --- /dev/null +++ b/src/api/dotnet/Microsoft.Z3.targets @@ -0,0 +1,11 @@ + + + + + + %(RecursiveDir)%(FileName)%(Extension) + PreserveNewest + + + + diff --git a/src/api/dotnet/Optimize.cs b/src/api/dotnet/Optimize.cs index 4713f414a..9a7bd3bd1 100644 --- a/src/api/dotnet/Optimize.cs +++ b/src/api/dotnet/Optimize.cs @@ -25,7 +25,7 @@ using System.Linq; namespace Microsoft.Z3 { /// - /// Object for managing optimizization context + /// Object for managing optimization context /// public class Optimize : Z3Object { diff --git a/src/api/dotnet/Quantifier.cs b/src/api/dotnet/Quantifier.cs index f4a889092..57d38e7c1 100644 --- a/src/api/dotnet/Quantifier.cs +++ b/src/api/dotnet/Quantifier.cs @@ -148,7 +148,7 @@ namespace Microsoft.Z3 get { - return new BoolExpr(Context, Native.Z3_get_quantifier_body(Context.nCtx, NativeObject)); + return (BoolExpr)Expr.Create(Context, Native.Z3_get_quantifier_body(Context.nCtx, NativeObject)); } } diff --git a/src/api/dotnet/core/README.txt b/src/api/dotnet/core/README.txt deleted file mode 100644 index fa274f72b..000000000 --- a/src/api/dotnet/core/README.txt +++ /dev/null @@ -1,15 +0,0 @@ -Z3 API for .NET Core - -Z3's .NET API uses Code Contracts, which are not included in .NET Core. The -enclosed file called DummyContracts.cs provides stubs for the Code Contracts -functions, so that the API will compile, but not perform any contract -checking. To build this using .NET core, run (in this directory): - -dotnet restore -dotnet build core.csproj -c Release - -If you are building with the cmake system, you should first -copy over files that are produced by the compiler into -this directory. You need to copy over Native.cs and Enumeration.cs - --- good luck! diff --git a/src/api/dotnet/core/core.csproj b/src/api/dotnet/core/core.csproj deleted file mode 100644 index 5fa3275cf..000000000 --- a/src/api/dotnet/core/core.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - netcoreapp1.0 - $(DefineConstants);DOTNET_CORE - portable - Microsoft.Z3 - Library - core - $(PackageTargetFallback);dnxcore50 - 1.0.4 - - - - - - - diff --git a/src/api/dotnet/dotnet35/Example/App.config b/src/api/dotnet/dotnet35/Example/App.config deleted file mode 100644 index 737ed23f2..000000000 --- a/src/api/dotnet/dotnet35/Example/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/api/dotnet/dotnet35/Example/Example.csproj b/src/api/dotnet/dotnet35/Example/Example.csproj deleted file mode 100644 index 842fbac41..000000000 --- a/src/api/dotnet/dotnet35/Example/Example.csproj +++ /dev/null @@ -1,78 +0,0 @@ - - - - - Debug - AnyCPU - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812} - Exe - Properties - Example - Example - v4.5.2 - 512 - true - - - AnyCPU - true - full - false - bin\Debug\ - TRACE;DEBUG;FRAMEWORK_LT_4 - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE;FRAMEWORK_LT_4 - prompt - 4 - - - true - bin\x64\Debug\ - TRACE;DEBUG;FRAMEWORK_LT_4 - full - x64 - prompt - MinimumRecommendedRules.ruleset - true - - - bin\x64\Release\ - TRACE;FRAMEWORK_LT_4 - true - pdbonly - x64 - prompt - MinimumRecommendedRules.ruleset - true - - - - Program.cs - - - - - - - - - {ec3db697-b734-42f7-9468-5b62821eeb5a} - Microsoft.Z3.NET35 - - - - - diff --git a/src/api/dotnet/dotnet35/Example/Properties/AssemblyInfo.cs b/src/api/dotnet/dotnet35/Example/Properties/AssemblyInfo.cs deleted file mode 100644 index ed0d8454f..000000000 --- a/src/api/dotnet/dotnet35/Example/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Example")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Example")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("2a8e577b-7b6d-4ca9-832a-ca2eec314812")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/api/dotnet/dotnet35/Microsoft.Z3.NET35.csproj b/src/api/dotnet/dotnet35/Microsoft.Z3.NET35.csproj deleted file mode 100644 index 15eaac445..000000000 --- a/src/api/dotnet/dotnet35/Microsoft.Z3.NET35.csproj +++ /dev/null @@ -1,347 +0,0 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {EC3DB697-B734-42F7-9468-5B62821EEB5A} - Library - Properties - Microsoft.Z3 - Microsoft.Z3 - v3.5 - 512 - - - 0 - - - true - full - false - Debug\ - TRACE;DEBUG;FRAMEWORK_LT_4 - prompt - 4 - true - Debug\Microsoft.Z3.XML - False - False - True - False - False - True - False - True - True - False - False - False - True - False - False - False - True - False - False - True - True - True - False - False - - - - - - - True - Full - %28none%29 - 2 - - - pdbonly - true - Release\ - FRAMEWORK_LT_4 - prompt - 4 - true - Release\Microsoft.Z3.xml - x86 - - - true - - - - - - - false - - - true - bin\x64\Debug\ - TRACE;DEBUG;FRAMEWORK_LT_4 - true - Debug\Microsoft.Z3.XML - full - x64 - prompt - MinimumRecommendedRules.ruleset - - - bin\x64\Release\ - FRAMEWORK_LT_4 - true - Release\Microsoft.Z3.xml - true - pdbonly - x64 - prompt - MinimumRecommendedRules.ruleset - - - - packages\Code.Contract.1.0.0\lib\net35\Microsoft.Contracts.dll - True - - - - - - - AlgebraicNum.cs - - - ApplyResult.cs - - - ArithExpr.cs - - - ArithSort.cs - - - ArrayExpr.cs - - - ArraySort.cs - - - AST.cs - - - ASTMap.cs - - - ASTVector.cs - - - BitVecExpr.cs - - - BitVecNum.cs - - - BitVecSort.cs - - - BoolExpr.cs - - - BoolSort.cs - - - Constructor.cs - - - ConstructorList.cs - - - Context.cs - - - DatatypeExpr.cs - - - DatatypeSort.cs - - - Deprecated.cs - - - Enumerations.cs - - - EnumSort.cs - - - Expr.cs - - - FiniteDomainExpr.cs - - - FiniteDomainNum.cs - - - FiniteDomainSort.cs - - - Fixedpoint.cs - - - FPExpr.cs - - - FPNum.cs - - - FPRMExpr.cs - - - FPRMNum.cs - - - FPRMSort.cs - - - FPSort.cs - - - FuncDecl.cs - - - FuncInterp.cs - - - Global.cs - - - Goal.cs - - - IDecRefQueue.cs - - - InterpolationContext.cs - - - IntExpr.cs - - - IntNum.cs - - - IntSort.cs - - - IntSymbol.cs - - - ListSort.cs - - - Log.cs - - - Model.cs - - - Native.cs - - - Optimize.cs - - - ParamDescrs.cs - - - Params.cs - - - Pattern.cs - - - Probe.cs - - - Quantifier.cs - - - RatNum.cs - - - RealExpr.cs - - - RealSort.cs - - - ReExpr.cs - - - RelationSort.cs - - - ReSort.cs - - - SeqExpr.cs - - - SeqSort.cs - - - SetSort.cs - - - Solver.cs - - - Sort.cs - - - Statistics.cs - - - Status.cs - - - StringSymbol.cs - - - Symbol.cs - - - Tactic.cs - - - TupleSort.cs - - - UninterpretedSort.cs - - - Version.cs - - - Z3Exception.cs - - - Z3Object.cs - - - - - - - - - - - diff --git a/src/api/dotnet/dotnet35/Microsoft.Z3.NET35.sln b/src/api/dotnet/dotnet35/Microsoft.Z3.NET35.sln deleted file mode 100644 index 1e33f136e..000000000 --- a/src/api/dotnet/dotnet35/Microsoft.Z3.NET35.sln +++ /dev/null @@ -1,48 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Z3.NET35", "Microsoft.Z3.NET35.csproj", "{EC3DB697-B734-42F7-9468-5B62821EEB5A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{2A8E577B-7B6D-4CA9-832A-CA2EEC314812}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Debug|x64.ActiveCfg = Debug|Any CPU - {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Debug|x64.Build.0 = Debug|Any CPU - {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Debug|x86.ActiveCfg = Debug|Any CPU - {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Debug|x86.Build.0 = Debug|Any CPU - {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Release|Any CPU.Build.0 = Release|Any CPU - {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Release|x64.ActiveCfg = Release|x64 - {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Release|x64.Build.0 = Release|x64 - {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Release|x86.ActiveCfg = Release|Any CPU - {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Release|x86.Build.0 = Release|Any CPU - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Debug|x64.ActiveCfg = Debug|Any CPU - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Debug|x64.Build.0 = Debug|Any CPU - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Debug|x86.ActiveCfg = Debug|Any CPU - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Debug|x86.Build.0 = Debug|Any CPU - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Release|Any CPU.Build.0 = Release|Any CPU - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Release|x64.ActiveCfg = Release|x64 - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Release|x64.Build.0 = Release|x64 - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Release|x86.ActiveCfg = Release|Any CPU - {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/api/dotnet/dotnet35/Properties/AssemblyInfo.cs b/src/api/dotnet/dotnet35/Properties/AssemblyInfo.cs deleted file mode 100644 index fb4319002..000000000 --- a/src/api/dotnet/dotnet35/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security.Permissions; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Z3 .NET Interface")] -[assembly: AssemblyDescription(".NET Interface to the Z3 Theorem Prover")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft Corporation")] -[assembly: AssemblyProduct("Z3")] -[assembly: AssemblyCopyright("Copyright (C) 2006-2015 Microsoft Corporation")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4853ed71-2078-40f4-8117-bc46646bce0e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("4.2.0.0")] -[assembly: AssemblyVersion("4.5.1.6031")] -[assembly: AssemblyFileVersion("4.5.1.6031")] diff --git a/src/api/dotnet/dotnet35/Properties/AssemblyInfo.cs.in b/src/api/dotnet/dotnet35/Properties/AssemblyInfo.cs.in deleted file mode 100644 index e5a85f16f..000000000 --- a/src/api/dotnet/dotnet35/Properties/AssemblyInfo.cs.in +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security.Permissions; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Z3 .NET Interface")] -[assembly: AssemblyDescription(".NET Interface to the Z3 Theorem Prover")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft Corporation")] -[assembly: AssemblyProduct("Z3")] -[assembly: AssemblyCopyright("Copyright (C) 2006-2015 Microsoft Corporation")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4853ed71-2078-40f4-8117-bc46646bce0e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("4.2.0.0")] -[assembly: AssemblyVersion("@VER_MAJOR@.@VER_MINOR@.@VER_BUILD@.@VER_REVISION@")] -[assembly: AssemblyFileVersion("@VER_MAJOR@.@VER_MINOR@.@VER_BUILD@.@VER_REVISION@")] diff --git a/src/api/dotnet/dotnet35/Readme.NET35 b/src/api/dotnet/dotnet35/Readme.NET35 deleted file mode 100644 index f8c2958ee..000000000 --- a/src/api/dotnet/dotnet35/Readme.NET35 +++ /dev/null @@ -1,10 +0,0 @@ -The default Z3 bindings for .NET are built for the .NET framework version 4. -Should the need arise, it is also possible to build them for .NET 3.5; the -instructions are as follows: - -In the project properties of Microsoft.Z3.csproj: -- Under 'Application': Change Target framework to .NET Framework 3.5 -- Under 'Build': Add FRAMEWORK_LT_4 to the condidional compilation symbols -- Remove the reference to System.Numerics -- Install the NuGet Package "Microsoft Code Contracts for Net3.5": - In the Package Manager Console enter Install-Package Code.Contract diff --git a/src/api/dotnet/dotnet35/packages.config b/src/api/dotnet/dotnet35/packages.config deleted file mode 100644 index bc9f365bc..000000000 --- a/src/api/dotnet/dotnet35/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index 302b3d5bd..c2d73ffb1 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -39,8 +39,7 @@ add_library(z3java SHARED ${Z3_JAVA_NATIVE_CPP}) target_link_libraries(z3java PRIVATE libz3) # FIXME: # Not sure if using all the flags used by the Z3 components is really necessary -# here. At the bare minimum setting _AMD64_ depending on the target is -# necessary but seeing as the Python build system uses all the flags used for building +# here. The Python build system uses all the flags used for building # Z3's components to build ``Native.cpp`` lets do the same for now. target_compile_options(z3java PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) target_compile_definitions(z3java PRIVATE ${Z3_COMPONENT_CXX_DEFINES}) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index ede494598..20c1c3737 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -3356,7 +3356,7 @@ public class Context implements AutoCloseable { } /** - * Create a numeral of FloatingPoint sort from a float. + * Create a numeral of FloatingPoint sort from a double. * @param v numeral value. * @param s FloatingPoint sort. * @throws Z3Exception @@ -3368,7 +3368,7 @@ public class Context implements AutoCloseable { /** * Create a numeral of FloatingPoint sort from an int. - * * @param v numeral value. + * @param v numeral value. * @param s FloatingPoint sort. * @throws Z3Exception **/ @@ -3380,8 +3380,8 @@ public class Context implements AutoCloseable { /** * Create a numeral of FloatingPoint sort from a sign bit and two integers. * @param sgn the sign. - * @param sig the significand. * @param exp the exponent. + * @param sig the significand. * @param s FloatingPoint sort. * @throws Z3Exception **/ @@ -3393,8 +3393,8 @@ public class Context implements AutoCloseable { /** * Create a numeral of FloatingPoint sort from a sign bit and two 64-bit integers. * @param sgn the sign. - * @param sig the significand. * @param exp the exponent. + * @param sig the significand. * @param s FloatingPoint sort. * @throws Z3Exception **/ @@ -3415,7 +3415,7 @@ public class Context implements AutoCloseable { } /** - * Create a numeral of FloatingPoint sort from a float. + * Create a numeral of FloatingPoint sort from a double. * @param v numeral value. * @param s FloatingPoint sort. * @throws Z3Exception @@ -3447,7 +3447,7 @@ public class Context implements AutoCloseable { **/ public FPNum mkFP(boolean sgn, int exp, int sig, FPSort s) { - return mkFPNumeral(sgn, sig, exp, s); + return mkFPNumeral(sgn, exp, sig, s); } /** @@ -3460,7 +3460,7 @@ public class Context implements AutoCloseable { **/ public FPNum mkFP(boolean sgn, long exp, long sig, FPSort s) { - return mkFPNumeral(sgn, sig, exp, s); + return mkFPNumeral(sgn, exp, sig, s); } diff --git a/src/api/java/Goal.java b/src/api/java/Goal.java index 903325850..f5fa35fdb 100644 --- a/src/api/java/Goal.java +++ b/src/api/java/Goal.java @@ -130,8 +130,7 @@ public class Goal extends Z3Object { int n = size(); BoolExpr[] res = new BoolExpr[n]; for (int i = 0; i < n; i++) - res[i] = new BoolExpr(getContext(), Native.goalFormula(getContext() - .nCtx(), getNativeObject(), i)); + res[i] = (BoolExpr) Expr.create(getContext(), Native.goalFormula(getContext().nCtx(), getNativeObject(), i)); return res; } diff --git a/src/api/java/Lambda.java b/src/api/java/Lambda.java index 45375fb29..cf9809a23 100644 --- a/src/api/java/Lambda.java +++ b/src/api/java/Lambda.java @@ -70,9 +70,9 @@ import com.microsoft.z3.enumerations.Z3_ast_kind; * * @throws Z3Exception **/ - public BoolExpr getBody() + public Expr getBody() { - return new BoolExpr(getContext(), Native.getQuantifierBody(getContext() + return Expr.create(getContext(), Native.getQuantifierBody(getContext() .nCtx(), getNativeObject())); } diff --git a/src/api/java/Quantifier.java b/src/api/java/Quantifier.java index 29483d1aa..73b726e8d 100644 --- a/src/api/java/Quantifier.java +++ b/src/api/java/Quantifier.java @@ -141,7 +141,7 @@ public class Quantifier extends BoolExpr **/ public BoolExpr getBody() { - return new BoolExpr(getContext(), Native.getQuantifierBody(getContext() + return (BoolExpr) Expr.create(getContext(), Native.getQuantifierBody(getContext() .nCtx(), getNativeObject())); } diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 9d8a9c923..5fb87bf64 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -2023,3 +2023,7 @@ let toggle_warning_messages = Z3native.toggle_warning_messages let enable_trace = Z3native.enable_trace let disable_trace = Z3native.enable_trace + +module Memory = struct + let reset = Z3native.reset_memory +end diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index ddc9f2a41..4b6d8bc25 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -3472,3 +3472,11 @@ val enable_trace : string -> unit Remarks: It is a NOOP otherwise. *) val disable_trace : string -> unit + + +(** Memory management **) +module Memory : +sig + (** Reset all allocated resources **) + val reset : unit -> unit +end diff --git a/src/api/ml/z3native_stubs.c.pre b/src/api/ml/z3native_stubs.c.pre index 71ee18ce9..5c1a3bd06 100644 --- a/src/api/ml/z3native_stubs.c.pre +++ b/src/api/ml/z3native_stubs.c.pre @@ -448,3 +448,21 @@ CAMLprim value DLL_PUBLIC n_set_internal_error_handler(value ctx_v) Z3_set_error_handler(ctx_p->ctx, MLErrorHandler); CAMLreturn(Val_unit); } + +CAMLprim DLL_PUBLIC value n_mk_config() { + CAMLparam0(); + CAMLlocal1(result); + Z3_config z3rv; + + /* invoke Z3 function */ + z3rv = Z3_mk_config(); + + if (z3rv == NULL) { + caml_raise_with_string(*caml_named_value("Z3EXCEPTION"), "Object allocation failed"); + } + + /* construct simple return value */ + result = caml_alloc_custom(&default_custom_ops, sizeof(Z3_config), 0, 1); *(Z3_config*)Data_custom_val(result) = z3rv; + /* cleanup and return */ + CAMLreturn(result); +} diff --git a/src/api/python/README.txt b/src/api/python/README.txt index 9312b1119..561b8dedc 100644 --- a/src/api/python/README.txt +++ b/src/api/python/README.txt @@ -1,8 +1,4 @@ -You can learn more about Z3Py at: -http://rise4fun.com/Z3Py/tutorial/guide - -On Windows, you must build Z3 before using Z3Py. -To build Z3, you should executed the following command +On Windows, to build Z3, you should executed the following command in the Z3 root directory at the Visual Studio Command Prompt msbuild /p:configuration=external @@ -12,8 +8,8 @@ If you are using a 64-bit Python interpreter, you should use msbuild /p:configuration=external /p:platform=x64 -On Linux and macOS, you must install Z3Py, before trying example.py. -To install Z3Py on Linux and macOS, you should execute the following +On Linux and macOS, you must install python bindings, before trying example.py. +To install python on Linux and macOS, you should execute the following command in the Z3 root directory sudo make install-z3py diff --git a/src/api/python/setup.py b/src/api/python/setup.py index 2a750fee6..2d5e5c75d 100644 --- a/src/api/python/setup.py +++ b/src/api/python/setup.py @@ -10,13 +10,14 @@ from distutils.util import get_platform from distutils.errors import LibError from distutils.command.build import build as _build from distutils.command.sdist import sdist as _sdist +from distutils.command.clean import clean as _clean from setuptools.command.develop import develop as _develop from setuptools.command.bdist_egg import bdist_egg as _bdist_egg build_env = dict(os.environ) build_env['PYTHON'] = sys.executable -build_env['CXXFLAGS'] = "-std=c++11" +build_env['CXXFLAGS'] = build_env.get('CXXFLAGS', '') + " -std=c++11" ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) SRC_DIR_LOCAL = os.path.join(ROOT_DIR, 'core') @@ -37,13 +38,23 @@ else: LIBRARY_FILE = "libz3.so" EXECUTABLE_FILE = "z3" +def rmtree(tree): + if os.path.exists(tree): + shutil.rmtree(tree, ignore_errors=False) + def _clean_bins(): """ Clean up the binary files and headers that are installed along with the bindings """ - shutil.rmtree(LIBS_DIR, ignore_errors=True) - shutil.rmtree(BINS_DIR, ignore_errors=True) - shutil.rmtree(HEADERS_DIR, ignore_errors=True) + rmtree(LIBS_DIR) + rmtree(BINS_DIR) + rmtree(HEADERS_DIR) + +def _clean_native_build(): + """ + Clean the "build" directory in the z3 native root + """ + rmtree(BUILD_DIR) def _z3_version(): post = os.getenv('Z3_VERSION_SUFFIX', '') @@ -146,10 +157,16 @@ class bdist_egg(_bdist_egg): class sdist(_sdist): def run(self): - self.execute(_clean_bins, (), msg="Cleaning binary files") + self.execute(_clean_bins, (), msg="Cleaning binary files and headers") self.execute(_copy_sources, (), msg="Copying source files") _sdist.run(self) +class clean(_clean): + def run(self): + self.execute(_clean_bins, (), msg="Cleaning binary files and headers") + self.execute(_clean_native_build, (), msg="Cleaning native build") + _clean.run(self) + # the build directory needs to exist #try: os.makedirs(os.path.join(ROOT_DIR, 'build')) #except OSError: pass @@ -178,7 +195,7 @@ setup( name='z3-solver', version=_z3_version(), description='an efficient SMT solver library', - long_description='Z3 is a theorem prover from Microsoft Research with support for bitvectors, booleans, arrays, floating point numbers, strings, and other data types.\n\nFor documentation, please read http://z3prover.github.io/api/html/z3.html\n\nIn the event of technical difficulties related to configuration, compiliation, or installation, please submit issues to https://github.com/angr/angr-z3', + long_description='Z3 is a theorem prover from Microsoft Research with support for bitvectors, booleans, arrays, floating point numbers, strings, and other data types.\n\nFor documentation, please read http://z3prover.github.io/api/html/z3.html\n\nIn the event of technical difficulties related to configuration, compilation, or installation, please submit issues to https://github.com/angr/angr-z3', author="The Z3 Theorem Prover Project", maintainer="Audrey Dutcher", maintainer_email="audrey@rhelmot.io", @@ -191,5 +208,5 @@ setup( 'z3': [os.path.join('lib', '*'), os.path.join('include', '*.h'), os.path.join('include', 'c++', '*.h')] }, data_files=[('bin',[os.path.join('bin',EXECUTABLE_FILE)])], - cmdclass={'build': build, 'develop': develop, 'sdist': sdist, 'bdist_egg': bdist_egg}, + cmdclass={'build': build, 'develop': develop, 'sdist': sdist, 'bdist_egg': bdist_egg, 'clean': clean}, ) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index ffd9c27a1..f8651ab90 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -482,6 +482,7 @@ def _to_ast_ref(a, ctx): else: return _to_expr_ref(a, ctx) + ######################################### # # Sorts @@ -3836,7 +3837,7 @@ def Extract(high, low, a): >>> Extract(6, 2, x).sort() BitVec(5) >>> simplify(Extract(StringVal("abcd"),2,1)) - "c" + c """ if isinstance(high, str): high = StringVal(high) @@ -4489,11 +4490,15 @@ def K(dom, v): return ArrayRef(Z3_mk_const_array(ctx.ref(), dom.ast, v.as_ast()), ctx) def Ext(a, b): - """Return extensionality index for arrays. + """Return extensionality index for one-dimensional arrays. + >> a, b = Consts('a b', SetSort(IntSort())) + >> Ext(a, b) + Ext(a, b) """ + ctx = a.ctx if __debug__: - _z3_assert(is_array(a) and is_array(b)) - return _to_expr_ref(Z3_mk_array_ext(ctx.ref(), a.as_ast(), b.as_ast())); + _z3_assert(is_array(a) and is_array(b), "arguments must be arrays") + return _to_expr_ref(Z3_mk_array_ext(ctx.ref(), a.as_ast(), b.as_ast()), ctx) def is_select(a): """Return `True` if `a` is a Z3 array select application. @@ -5339,7 +5344,7 @@ class Goal(Z3PPObject): def __copy__(self): return self.translate(self.ctx) - def __deepcopy__(self): + def __deepcopy__(self, memo={}): return self.translate(self.ctx) def simplify(self, *arguments, **keywords): @@ -5527,7 +5532,7 @@ class AstVector(Z3PPObject): def __copy__(self): return self.translate(self.ctx) - def __deepcopy__(self): + def __deepcopy__(self, memo={}): return self.translate(self.ctx) def __repr__(self): @@ -5871,7 +5876,7 @@ class FuncInterp(Z3PPObject): def __copy__(self): return self.translate(self.ctx) - def __deepcopy__(self): + def __deepcopy__(self, memo={}): return self.translate(self.ctx) def as_list(self): @@ -6167,7 +6172,7 @@ class ModelRef(Z3PPObject): def __copy__(self): return self.translate(self.ctx) - def __deepcopy__(self): + def __deepcopy__(self, memo={}): return self.translate(self.ctx) def Model(ctx = None): @@ -6664,17 +6669,11 @@ class Solver(Z3PPObject): def from_file(self, filename): """Parse assertions from a file""" - try: - Z3_solver_from_file(self.ctx.ref(), self.solver, filename) - except Z3Exception as e: - _handle_parse_error(e, self.ctx) + Z3_solver_from_file(self.ctx.ref(), self.solver, filename) def from_string(self, s): """Parse assertions from a string""" - try: - Z3_solver_from_string(self.ctx.ref(), self.solver, s) - except Z3Exception as e: - _handle_parse_error(e, self.ctx) + Z3_solver_from_string(self.ctx.ref(), self.solver, s) def cube(self, vars = None): """Get set of cubes @@ -6732,6 +6731,25 @@ class Solver(Z3PPObject): """ return AstVector(Z3_solver_get_non_units(self.ctx.ref(), self.solver), self.ctx) + def trail_levels(self): + """Return trail and decision levels of the solver state after a check() call. + """ + trail = self.trail() + levels = (ctypes.c_uint * len(trail))() + Z3_solver_get_levels(self.ctx.ref(), self.solver, trail.vector, len(trail), levels) + return trail, levels + + def trail(self): + """Return trail of the solver state after a check() call. + """ + return AstVector(Z3_solver_get_trail(self.ctx.ref(), self.solver), self.ctx) + + def set_activity(self, lit, act): + """Set activity of literal on solver object. + This influences the case split order of the variable. + """ + Z3_solver_set_activity(self.ctx.ref(), self.solver, lit.ast, act) + def statistics(self): """Return statistics for the last `check()`. @@ -6791,7 +6809,7 @@ class Solver(Z3PPObject): def __copy__(self): return self.translate(self.ctx) - def __deepcopy__(self): + def __deepcopy__(self, memo={}): return self.translate(self.ctx) def sexpr(self): @@ -6805,6 +6823,10 @@ class Solver(Z3PPObject): """ return Z3_solver_to_string(self.ctx.ref(), self.solver) + def dimacs(self): + """Return a textual representation of the solver in DIMACS format.""" + return Z3_solver_to_dimacs_string(self.ctx.ref(), self.solver) + def to_smt2(self): """return SMTLIB2 formatted benchmark for solver's assertions""" es = self.assertions() @@ -7063,17 +7085,11 @@ class Fixedpoint(Z3PPObject): def parse_string(self, s): """Parse rules and queries from a string""" - try: - return AstVector(Z3_fixedpoint_from_string(self.ctx.ref(), self.fixedpoint, s), self.ctx) - except Z3Exception as e: - _handle_parse_error(e, self.ctx) + return AstVector(Z3_fixedpoint_from_string(self.ctx.ref(), self.fixedpoint, s), self.ctx) def parse_file(self, f): """Parse rules and queries from a file""" - try: - return AstVector(Z3_fixedpoint_from_file(self.ctx.ref(), self.fixedpoint, f), self.ctx) - except Z3Exception as e: - _handle_parse_error(e, self.ctx) + return AstVector(Z3_fixedpoint_from_file(self.ctx.ref(), self.fixedpoint, f), self.ctx) def get_rules(self): """retrieve rules that have been added to fixedpoint context""" @@ -7330,6 +7346,35 @@ class Optimize(Z3PPObject): self.add(fml) return self + def assert_and_track(self, a, p): + """Assert constraint `a` and track it in the unsat core using the Boolean constant `p`. + + If `p` is a string, it will be automatically converted into a Boolean constant. + + >>> x = Int('x') + >>> p3 = Bool('p3') + >>> s = Optimize() + >>> s.assert_and_track(x > 0, 'p1') + >>> s.assert_and_track(x != 1, 'p2') + >>> s.assert_and_track(x < 0, p3) + >>> print(s.check()) + unsat + >>> c = s.unsat_core() + >>> len(c) + 2 + >>> Bool('p1') in c + True + >>> Bool('p2') in c + False + >>> p3 in c + True + """ + if isinstance(p, str): + p = Bool(p, self.ctx) + _z3_assert(isinstance(a, BoolRef), "Boolean expression expected") + _z3_assert(isinstance(p, BoolRef) and is_const(p), "Boolean expression expected") + Z3_optimize_assert_and_track(self.ctx.ref(), self.optimize, a.as_ast(), p.as_ast()) + def add_soft(self, arg, weight = "1", id = None): """Add soft constraint with optional weight and optional identifier. If no weight is supplied, then the penalty for violating the soft constraint @@ -7410,17 +7455,11 @@ class Optimize(Z3PPObject): def from_file(self, filename): """Parse assertions and objectives from a file""" - try: - Z3_optimize_from_file(self.ctx.ref(), self.optimize, filename) - except Z3Exception as e: - _handle_parse_error(e, self.ctx) + Z3_optimize_from_file(self.ctx.ref(), self.optimize, filename) def from_string(self, s): """Parse assertions and objectives from a string""" - try: - Z3_optimize_from_string(self.ctx.ref(), self.optimize, s) - except Z3Exception as e: - _handle_parse_error(e, self.ctx) + Z3_optimize_from_string(self.ctx.ref(), self.optimize, s) def assertions(self): """Return an AST vector containing all added constraints.""" @@ -9914,6 +9953,8 @@ class SeqRef(ExprRef): def as_string(self): """Return a string representation of sequence expression.""" + if self.is_string_value(): + return Z3_get_string(self.ctx_ref(), self.as_ast()) return Z3_ast_to_string(self.ctx_ref(), self.as_ast()) @@ -9993,8 +10034,6 @@ def Strings(names, ctx=None): def Empty(s): """Create the empty sequence of the given sort >>> e = Empty(StringSort()) - >>> print(e) - "" >>> e2 = StringVal("") >>> print(e.eq(e2)) True @@ -10080,7 +10119,7 @@ def Replace(s, src, dst): """Replace the first occurrence of 'src' by 'dst' in 's' >>> r = Replace("aaa", "a", "b") >>> simplify(r) - "baa" + baa """ ctx = _get_ctx2(dst, s) if ctx is None and is_expr(src): diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 6e3ce57a1..e1710b499 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1515,19 +1515,19 @@ extern "C" { although some parameters can be changed using #Z3_update_param_value. All main interaction with Z3 happens in the context of a \c Z3_context. - In contrast to #Z3_mk_context_rc, the life time of Z3_ast objects + In contrast to #Z3_mk_context_rc, the life time of \c Z3_ast objects are determined by the scope level of #Z3_solver_push and #Z3_solver_pop. - In other words, a Z3_ast object remains valid until there is a - call to Z3_solver_pop that takes the current scope below the level where + In other words, a \c Z3_ast object remains valid until there is a + call to #Z3_solver_pop that takes the current scope below the level where the object was created. - Note that all other reference counted objects, including Z3_model, - Z3_solver, Z3_func_interp have to be managed by the caller. + Note that all other reference counted objects, including \c Z3_model, + \c Z3_solver, \c Z3_func_interp have to be managed by the caller. Their reference counts are not handled by the context. Further remarks: - - Z3_sort, Z3_func_decl, Z3_app, Z3_pattern are Z3_ast's. - - Z3 uses hash-consing, i.e., when the same Z3_ast is created twice, + - \c Z3_sort, \c Z3_func_decl, \c Z3_app, \c Z3_pattern are \c Z3_ast's. + - Z3 uses hash-consing, i.e., when the same \c Z3_ast is created twice, Z3 will return the same pointer twice. \sa Z3_del_context @@ -1540,20 +1540,20 @@ extern "C" { \brief Create a context using the given configuration. This function is similar to #Z3_mk_context. However, in the context returned by this function, the user - is responsible for managing Z3_ast reference counters. + is responsible for managing \c Z3_ast reference counters. Managing reference counters is a burden and error-prone, but allows the user to use the memory more efficiently. - The user must invoke #Z3_inc_ref for any Z3_ast returned - by Z3, and #Z3_dec_ref whenever the Z3_ast is not needed + The user must invoke #Z3_inc_ref for any \c Z3_ast returned + by Z3, and #Z3_dec_ref whenever the \c Z3_ast is not needed anymore. This idiom is similar to the one used in BDD (binary decision diagrams) packages such as CUDD. Remarks: - - Z3_sort, Z3_func_decl, Z3_app, Z3_pattern are Z3_ast's. + - \c Z3_sort, \c Z3_func_decl, \c Z3_app, \c Z3_pattern are \c Z3_ast's. - After a context is created, the configuration cannot be changed. - All main interaction with Z3 happens in the context of a \c Z3_context. - - Z3 uses hash-consing, i.e., when the same Z3_ast is created twice, + - Z3 uses hash-consing, i.e., when the same \c Z3_ast is created twice, Z3 will return the same pointer twice. def_API('Z3_mk_context_rc', CONTEXT, (_in(CONFIG),)) @@ -1615,7 +1615,7 @@ extern "C" { Starting at Z3 4.0, parameter sets are used to configure many components such as: simplifiers, tactics, solvers, etc. - \remark Reference counting must be used to manage parameter sets, even when the Z3_context was + \remark Reference counting must be used to manage parameter sets, even when the \c Z3_context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_params', PARAMS, (_in(CONTEXT),)) @@ -4094,7 +4094,7 @@ extern "C" { The remaining fields are left unchanged. It is the record equivalent of an array store (see \sa Z3_mk_store). If the datatype has more than one constructor, then the update function - behaves as identity if there is a miss-match between the accessor and + behaves as identity if there is a mismatch between the accessor and constructor. For example ((_ update-field car) nil 1) is nil, while ((_ update-field car) (cons 2 nil) 1) is (cons 1 nil). @@ -4416,7 +4416,7 @@ extern "C" { bool Z3_API Z3_is_well_sorted(Z3_context c, Z3_ast t); /** - \brief Return Z3_L_TRUE if \c a is true, Z3_L_FALSE if it is false, and Z3_L_UNDEF otherwise. + \brief Return \c Z3_L_TRUE if \c a is true, \c Z3_L_FALSE if it is false, and \c Z3_L_UNDEF otherwise. def_API('Z3_get_bool_value', INT, (_in(CONTEXT), _in(AST))) */ @@ -4998,7 +4998,7 @@ extern "C" { Z3_ast_vector Z3_API Z3_model_get_sort_universe(Z3_context c, Z3_model m, Z3_sort s); /** - \brief translate model from context c to context \c dst. + \brief translate model from context \c c to context \c dst. def_API('Z3_model_translate', MODEL, (_in(CONTEXT), _in(MODEL), _in(CONTEXT))) */ @@ -6213,6 +6213,13 @@ extern "C" { */ Z3_ast_vector Z3_API Z3_solver_get_units(Z3_context c, Z3_solver s); + /** + \brief Return the trail modulo model conversion, in order of decision level + The decision level can be retrieved using \c Z3_solver_get_level based on the trail. + + def_API('Z3_solver_get_trail', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) + */ + Z3_ast_vector Z3_API Z3_solver_get_trail(Z3_context c, Z3_solver s); /** \brief Return the set of non units in the solver state. @@ -6221,13 +6228,28 @@ extern "C" { */ Z3_ast_vector Z3_API Z3_solver_get_non_units(Z3_context c, Z3_solver s); + /** + \brief retrieve the decision depth of Boolean literals (variables or their negations). + Assumes a check-sat call and no other calls (to extract models) have been invoked. + + def_API('Z3_solver_get_levels', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST_VECTOR), _in(UINT), _in_array(3, UINT))) + */ + void Z3_API Z3_solver_get_levels(Z3_context c, Z3_solver s, Z3_ast_vector literals, unsigned sz, unsigned levels[]); + + /** + \brief set activity score associated with literal. + + def_API('Z3_solver_set_activity', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST), _in(DOUBLE))) + */ + void Z3_API Z3_solver_set_activity(Z3_context c, Z3_solver s, Z3_ast l, double activity); + /** \brief Check whether the assertions in a given solver are consistent or not. The function #Z3_solver_get_model retrieves a model if the assertions is satisfiable (i.e., the result is \c Z3_L_TRUE) and model construction is enabled. - Note that if the call returns Z3_L_UNDEF, Z3 does not + Note that if the call returns \c Z3_L_UNDEF, Z3 does not ensure that calls to #Z3_solver_get_model succeed and any models produced in this case are not guaranteed to satisfy the assertions. @@ -6269,7 +6291,7 @@ extern "C" { the current context implies that they are equal. A side-effect of the function is a satisfiability check on the assertions on the solver that is passed in. - The function return Z3_L_FALSE if the current assertions are not satisfiable. + The function return \c Z3_L_FALSE if the current assertions are not satisfiable. def_API('Z3_get_implied_equalities', INT, (_in(CONTEXT), _in(SOLVER), _in(UINT), _in_array(2, AST), _out_array(2, UINT))) */ @@ -6342,7 +6364,7 @@ extern "C" { Z3_ast_vector Z3_API Z3_solver_get_unsat_core(Z3_context c, Z3_solver s); /** - \brief Return a brief justification for an "unknown" result (i.e., Z3_L_UNDEF) for + \brief Return a brief justification for an "unknown" result (i.e., \c Z3_L_UNDEF) for the commands #Z3_solver_check and #Z3_solver_check_assumptions def_API('Z3_solver_get_reason_unknown', STRING, (_in(CONTEXT), _in(SOLVER))) @@ -6368,6 +6390,14 @@ extern "C" { */ Z3_string Z3_API Z3_solver_to_string(Z3_context c, Z3_solver s); + /** + \brief Convert a solver into a DIMACS formatted string. + \sa Z3_goal_to_diamcs_string for requirements. + + def_API('Z3_solver_to_dimacs_string', STRING, (_in(CONTEXT), _in(SOLVER))) + */ + Z3_string Z3_API Z3_solver_to_dimacs_string(Z3_context c, Z3_solver s); + /*@}*/ /** @name Statistics */ diff --git a/src/api/z3_fixedpoint.h b/src/api/z3_fixedpoint.h index 393cba224..54a42e9bf 100644 --- a/src/api/z3_fixedpoint.h +++ b/src/api/z3_fixedpoint.h @@ -106,9 +106,9 @@ extern "C" { \endcode query returns - - Z3_L_FALSE if the query is unsatisfiable. - - Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer. - - Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed. + - \c Z3_L_FALSE if the query is unsatisfiable. + - \c Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer. + - \c Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed. def_API('Z3_fixedpoint_query', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST))) */ @@ -120,9 +120,9 @@ extern "C" { The queries are encoded as relations (function declarations). query returns - - Z3_L_FALSE if the query is unsatisfiable. - - Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer. - - Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed. + - \c Z3_L_FALSE if the query is unsatisfiable. + - \c Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer. + - \c Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed. def_API('Z3_fixedpoint_query_relations', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(UINT), _in_array(2, FUNC_DECL))) */ @@ -138,8 +138,8 @@ extern "C" { Each conjunct encodes values of the bound variables of the query that are satisfied. In PDR mode, the returned answer is a single conjunction. - When used in Datalog mode the previous call to #Z3_fixedpoint_query must have returned Z3_L_TRUE. - When used with the PDR engine, the previous call must have been either Z3_L_TRUE or Z3_L_FALSE. + When used in Datalog mode the previous call to #Z3_fixedpoint_query must have returned \c Z3_L_TRUE. + When used with the PDR engine, the previous call must have been either \c Z3_L_TRUE or \c Z3_L_FALSE. def_API('Z3_fixedpoint_get_answer', AST, (_in(CONTEXT), _in(FIXEDPOINT))) */ @@ -148,7 +148,7 @@ extern "C" { /** \brief Retrieve a string that describes the last status returned by #Z3_fixedpoint_query. - Use this method when #Z3_fixedpoint_query returns Z3_L_UNDEF. + Use this method when #Z3_fixedpoint_query returns \c Z3_L_UNDEF. def_API('Z3_fixedpoint_get_reason_unknown', STRING, (_in(CONTEXT), _in(FIXEDPOINT) )) */ @@ -395,7 +395,7 @@ extern "C" { Z3_fixedpoint_predecessor_eh predecessor_eh, Z3_fixedpoint_unfold_eh unfold_eh); - void Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl); + void Z3_API Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl); /*@}*/ /*@}*/ diff --git a/src/api/z3_fpa.h b/src/api/z3_fpa.h index cc2091caa..1eaa1f64e 100644 --- a/src/api/z3_fpa.h +++ b/src/api/z3_fpa.h @@ -134,7 +134,7 @@ extern "C" { \param ebits number of exponent bits \param sbits number of significand bits - \remark ebits must be larger than 1 and sbits must be larger than 2. + \remark \c ebits must be larger than 1 and \c sbits must be larger than 2. def_API('Z3_mk_fpa_sort', SORT, (_in(CONTEXT), _in(UINT), _in(UINT))) */ @@ -213,7 +213,7 @@ extern "C" { Z3_sort Z3_API Z3_mk_fpa_sort_128(Z3_context c); /** - \brief Create a floating-point NaN of sort s. + \brief Create a floating-point NaN of sort \c s. \param c logical context \param s target sort @@ -223,7 +223,7 @@ extern "C" { Z3_ast Z3_API Z3_mk_fpa_nan(Z3_context c, Z3_sort s); /** - \brief Create a floating-point infinity of sort s. + \brief Create a floating-point infinity of sort \c s. \param c logical context \param s target sort @@ -236,7 +236,7 @@ extern "C" { Z3_ast Z3_API Z3_mk_fpa_inf(Z3_context c, Z3_sort s, bool negative); /** - \brief Create a floating-point zero of sort s. + \brief Create a floating-point zero of sort \c s. \param c logical context \param s target sort @@ -252,7 +252,7 @@ extern "C" { \brief Create an expression of FloatingPoint sort from three bit-vector expressions. This is the operator named `fp' in the SMT FP theory definition. - Note that \c sign is required to be a bit-vector of size 1. Significand and exponent + Note that \c sgn is required to be a bit-vector of size 1. Significand and exponent are required to be longer than 1 and 2 respectively. The FloatingPoint sort of the resulting expression is automatically determined from the bit-vector sizes of the arguments. The exponent is assumed to be in IEEE-754 biased representation. @@ -276,7 +276,7 @@ extern "C" { \param v value \param ty sort - ty must be a FloatingPoint sort + \c ty must be a FloatingPoint sort \sa Z3_mk_numeral @@ -294,7 +294,7 @@ extern "C" { \param v value \param ty sort - ty must be a FloatingPoint sort + \c ty must be a FloatingPoint sort \sa Z3_mk_numeral @@ -309,7 +309,7 @@ extern "C" { \param v value \param ty result sort - ty must be a FloatingPoint sort + \c ty must be a FloatingPoint sort \sa Z3_mk_numeral @@ -326,7 +326,7 @@ extern "C" { \param exp exponent \param ty result sort - ty must be a FloatingPoint sort + \c ty must be a FloatingPoint sort \sa Z3_mk_numeral @@ -343,7 +343,7 @@ extern "C" { \param exp exponent \param ty result sort - ty must be a FloatingPoint sort + \c ty must be a FloatingPoint sort \sa Z3_mk_numeral @@ -379,7 +379,7 @@ extern "C" { \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort - rm must be of RoundingMode sort, t1 and t2 must have the same FloatingPoint sort. + \c rm must be of RoundingMode sort, \c t1 and \c t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_add', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST))) */ @@ -393,7 +393,7 @@ extern "C" { \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort - rm must be of RoundingMode sort, t1 and t2 must have the same FloatingPoint sort. + \c rm must be of RoundingMode sort, \c t1 and \c t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_sub', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST))) */ @@ -407,7 +407,7 @@ extern "C" { \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort - rm must be of RoundingMode sort, t1 and t2 must have the same FloatingPoint sort. + \c rm must be of RoundingMode sort, \c t1 and \c t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_mul', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST))) */ @@ -421,7 +421,7 @@ extern "C" { \param t1 term of FloatingPoint sort. \param t2 term of FloatingPoint sort - The nodes rm must be of RoundingMode sort t1 and t2 must have the same FloatingPoint sort. + The nodes \c rm must be of RoundingMode sort, \c t1 and \c t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_div', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST))) */ @@ -436,9 +436,9 @@ extern "C" { \param t2 term of FloatingPoint sort \param t3 term of FloatingPoint sort - The result is round((t1 * t2) + t3) + The result is \ccode{round((t1 * t2) + t3)}. - rm must be of RoundingMode sort, t1, t2, and t3 must have the same FloatingPoint sort. + \c rm must be of RoundingMode sort, \c t1, \c t2, and \c t3 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_fma', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST),_in(AST))) */ @@ -451,7 +451,7 @@ extern "C" { \param rm term of RoundingMode sort \param t term of FloatingPoint sort - rm must be of RoundingMode sort, t must have FloatingPoint sort. + \c rm must be of RoundingMode sort, \c t must have FloatingPoint sort. def_API('Z3_mk_fpa_sqrt', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ @@ -464,7 +464,7 @@ extern "C" { \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort - t1 and t2 must have the same FloatingPoint sort. + \c t1 and \c t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_rem', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ @@ -478,7 +478,7 @@ extern "C" { \param rm term of RoundingMode sort \param t term of FloatingPoint sort - t must be of FloatingPoint sort. + \c t must be of FloatingPoint sort. def_API('Z3_mk_fpa_round_to_integral', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ @@ -491,7 +491,7 @@ extern "C" { \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort - t1, t2 must have the same FloatingPoint sort. + \c t1, \c t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_min', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ @@ -504,7 +504,7 @@ extern "C" { \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort - t1, t2 must have the same FloatingPoint sort. + \c t1, \c t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_max', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ @@ -517,7 +517,7 @@ extern "C" { \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort - t1 and t2 must have the same FloatingPoint sort. + \c t1 and \c t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_leq', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ @@ -530,7 +530,7 @@ extern "C" { \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort - t1 and t2 must have the same FloatingPoint sort. + \c t1 and \c t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_lt', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ @@ -543,7 +543,7 @@ extern "C" { \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort - t1 and t2 must have the same FloatingPoint sort. + \c t1 and \c t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_geq', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ @@ -556,7 +556,7 @@ extern "C" { \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort - t1 and t2 must have the same FloatingPoint sort. + \c t1 and \c t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_gt', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ @@ -569,93 +569,93 @@ extern "C" { \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort - Note that this is IEEE 754 equality (as opposed to SMT-LIB =). + Note that this is IEEE 754 equality (as opposed to SMT-LIB \ccode{=}). - t1 and t2 must have the same FloatingPoint sort. + \c t1 and \c t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_eq', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_eq(Z3_context c, Z3_ast t1, Z3_ast t2); /** - \brief Predicate indicating whether t is a normal floating-point number. + \brief Predicate indicating whether \c t is a normal floating-point number. \param c logical context \param t term of FloatingPoint sort - t must have FloatingPoint sort. + \c t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_normal', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_normal(Z3_context c, Z3_ast t); /** - \brief Predicate indicating whether t is a subnormal floating-point number. + \brief Predicate indicating whether \c t is a subnormal floating-point number. \param c logical context \param t term of FloatingPoint sort - t must have FloatingPoint sort. + \c t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_subnormal', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_subnormal(Z3_context c, Z3_ast t); /** - \brief Predicate indicating whether t is a floating-point number with zero value, i.e., +zero or -zero. + \brief Predicate indicating whether \c t is a floating-point number with zero value, i.e., +zero or -zero. \param c logical context \param t term of FloatingPoint sort - t must have FloatingPoint sort. + \c t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_zero', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_zero(Z3_context c, Z3_ast t); /** - \brief Predicate indicating whether t is a floating-point number representing +oo or -oo. + \brief Predicate indicating whether \c t is a floating-point number representing +oo or -oo. \param c logical context \param t term of FloatingPoint sort - t must have FloatingPoint sort. + \c t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_infinite', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_infinite(Z3_context c, Z3_ast t); /** - \brief Predicate indicating whether t is a NaN. + \brief Predicate indicating whether \c t is a NaN. \param c logical context \param t term of FloatingPoint sort - t must have FloatingPoint sort. + \c t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_nan', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_nan(Z3_context c, Z3_ast t); /** - \brief Predicate indicating whether t is a negative floating-point number. + \brief Predicate indicating whether \c t is a negative floating-point number. \param c logical context \param t term of FloatingPoint sort - t must have FloatingPoint sort. + \c t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_negative', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_negative(Z3_context c, Z3_ast t); /** - \brief Predicate indicating whether t is a positive floating-point number. + \brief Predicate indicating whether \c t is a positive floating-point number. \param c logical context \param t term of FloatingPoint sort - t must have FloatingPoint sort. + \c t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_positive', AST, (_in(CONTEXT),_in(AST))) */ @@ -664,15 +664,15 @@ extern "C" { /** \brief Conversion of a single IEEE 754-2008 bit-vector into a floating-point number. - Produces a term that represents the conversion of a bit-vector term bv to a - floating-point term of sort s. + Produces a term that represents the conversion of a bit-vector term \c bv to a + floating-point term of sort \c s. \param c logical context \param bv a bit-vector term \param s floating-point sort - s must be a FloatingPoint sort, t must be of bit-vector sort, and the bit-vector - size of bv must be equal to ebits+sbits of s. The format of the bit-vector is + \c s must be a FloatingPoint sort, \c t must be of bit-vector sort, and the bit-vector + size of \c bv must be equal to \ccode{ebits+sbits} of \c s. The format of the bit-vector is as defined by the IEEE 754-2008 interchange format. def_API('Z3_mk_fpa_to_fp_bv', AST, (_in(CONTEXT),_in(AST),_in(SORT))) @@ -682,16 +682,16 @@ extern "C" { /** \brief Conversion of a FloatingPoint term into another term of different FloatingPoint sort. - Produces a term that represents the conversion of a floating-point term t to a - floating-point term of sort s. If necessary, the result will be rounded according - to rounding mode rm. + Produces a term that represents the conversion of a floating-point term \c t to a + floating-point term of sort \c s. If necessary, the result will be rounded according + to rounding mode \c rm. \param c logical context \param rm term of RoundingMode sort \param t term of FloatingPoint sort \param s floating-point sort - s must be a FloatingPoint sort, rm must be of RoundingMode sort, t must be of floating-point sort. + \c s must be a FloatingPoint sort, \c rm must be of RoundingMode sort, \c t must be of floating-point sort. def_API('Z3_mk_fpa_to_fp_float', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(SORT))) */ @@ -700,16 +700,16 @@ extern "C" { /** \brief Conversion of a term of real sort into a term of FloatingPoint sort. - Produces a term that represents the conversion of term t of real sort into a - floating-point term of sort s. If necessary, the result will be rounded according - to rounding mode rm. + Produces a term that represents the conversion of term \c t of real sort into a + floating-point term of sort \c s. If necessary, the result will be rounded according + to rounding mode \c rm. \param c logical context \param rm term of RoundingMode sort \param t term of Real sort \param s floating-point sort - s must be a FloatingPoint sort, rm must be of RoundingMode sort, t must be of real sort. + \c s must be a FloatingPoint sort, \c rm must be of RoundingMode sort, \c t must be of real sort. def_API('Z3_mk_fpa_to_fp_real', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(SORT))) */ @@ -718,17 +718,17 @@ extern "C" { /** \brief Conversion of a 2's complement signed bit-vector term into a term of FloatingPoint sort. - Produces a term that represents the conversion of the bit-vector term t into a - floating-point term of sort s. The bit-vector t is taken to be in signed + Produces a term that represents the conversion of the bit-vector term \c t into a + floating-point term of sort \c s. The bit-vector \c t is taken to be in signed 2's complement format. If necessary, the result will be rounded according - to rounding mode rm. + to rounding mode \c rm. \param c logical context \param rm term of RoundingMode sort \param t term of bit-vector sort \param s floating-point sort - s must be a FloatingPoint sort, rm must be of RoundingMode sort, t must be of bit-vector sort. + \c s must be a FloatingPoint sort, \c rm must be of RoundingMode sort, \c t must be of bit-vector sort. def_API('Z3_mk_fpa_to_fp_signed', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(SORT))) */ @@ -737,17 +737,17 @@ extern "C" { /** \brief Conversion of a 2's complement unsigned bit-vector term into a term of FloatingPoint sort. - Produces a term that represents the conversion of the bit-vector term t into a - floating-point term of sort s. The bit-vector t is taken to be in unsigned + Produces a term that represents the conversion of the bit-vector term \c t into a + floating-point term of sort \c s. The bit-vector \c t is taken to be in unsigned 2's complement format. If necessary, the result will be rounded according - to rounding mode rm. + to rounding mode \c rm. \param c logical context \param rm term of RoundingMode sort \param t term of bit-vector sort \param s floating-point sort - s must be a FloatingPoint sort, rm must be of RoundingMode sort, t must be of bit-vector sort. + \c s must be a FloatingPoint sort, \c rm must be of RoundingMode sort, \c t must be of bit-vector sort. def_API('Z3_mk_fpa_to_fp_unsigned', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(SORT))) */ @@ -756,9 +756,9 @@ extern "C" { /** \brief Conversion of a floating-point term into an unsigned bit-vector. - Produces a term that represents the conversion of the floating-point term t into a - bit-vector term of size sz in unsigned 2's complement format. If necessary, the result - will be rounded according to rounding mode rm. + Produces a term that represents the conversion of the floating-point term \c t into a + bit-vector term of size \c sz in unsigned 2's complement format. If necessary, the result + will be rounded according to rounding mode \c rm. \param c logical context \param rm term of RoundingMode sort @@ -772,9 +772,9 @@ extern "C" { /** \brief Conversion of a floating-point term into a signed bit-vector. - Produces a term that represents the conversion of the floating-point term t into a - bit-vector term of size sz in signed 2's complement format. If necessary, the result - will be rounded according to rounding mode rm. + Produces a term that represents the conversion of the floating-point term \c t into a + bit-vector term of size \c sz in signed 2's complement format. If necessary, the result + will be rounded according to rounding mode \c rm. \param c logical context \param rm term of RoundingMode sort @@ -788,7 +788,7 @@ extern "C" { /** \brief Conversion of a floating-point term into a real-numbered term. - Produces a term that represents the conversion of the floating-point term t into a + Produces a term that represents the conversion of the floating-point term \c t into a real number. Note that this type of conversion will often result in non-linear constraints over real terms. @@ -936,7 +936,7 @@ extern "C" { \param c logical context \param t a floating-point numeral - Remarks: The significand s is always 0.0 <= s < 2.0; the resulting string is long + Remarks: The significand \c s is always \ccode{0.0 <= s < 2.0}; the resulting string is long enough to represent the real significand precisely. def_API('Z3_fpa_get_numeral_significand_string', STRING, (_in(CONTEXT), _in(AST))) @@ -951,8 +951,8 @@ extern "C" { \param n pointer to output uint64 Remarks: This function extracts the significand bits in `t`, without the - hidden bit or normalization. Sets the Z3_INVALID_ARG error code if the - significand does not fit into a uint64. NaN is an invalid argument. + hidden bit or normalization. Sets the \c Z3_INVALID_ARG error code if the + significand does not fit into a \c uint64. NaN is an invalid argument. def_API('Z3_fpa_get_numeral_significand_uint64', BOOL, (_in(CONTEXT), _in(AST), _out(UINT64))) */ @@ -1007,7 +1007,7 @@ extern "C" { \param c logical context \param t term of FloatingPoint sort - t must have FloatingPoint sort. The size of the resulting bit-vector is automatically + \c t must have FloatingPoint sort. The size of the resulting bit-vector is automatically determined. Note that IEEE 754-2008 allows multiple different representations of NaN. This conversion @@ -1021,9 +1021,9 @@ extern "C" { /** \brief Conversion of a real-sorted significand and an integer-sorted exponent into a term of FloatingPoint sort. - Produces a term that represents the conversion of sig * 2^exp into a - floating-point term of sort s. If necessary, the result will be rounded - according to rounding mode rm. + Produces a term that represents the conversion of \ccode{sig * 2^exp} into a + floating-point term of sort \c s. If necessary, the result will be rounded + according to rounding mode \c rm. \param c logical context \param rm term of RoundingMode sort @@ -1031,7 +1031,7 @@ extern "C" { \param sig significand term of Real sort \param s FloatingPoint sort - s must be a FloatingPoint sort, rm must be of RoundingMode sort, exp must be of int sort, sig must be of real sort. + \c s must be a FloatingPoint sort, \c rm must be of RoundingMode sort, \c exp must be of int sort, \c sig must be of real sort. def_API('Z3_mk_fpa_to_fp_int_real', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST),_in(SORT))) */ diff --git a/src/api/z3_macros.h b/src/api/z3_macros.h index c74144e90..d1ac18804 100644 --- a/src/api/z3_macros.h +++ b/src/api/z3_macros.h @@ -19,7 +19,3 @@ Copyright (c) 2015 Microsoft Corporation #ifndef DEFINE_TYPE #define DEFINE_TYPE(T) typedef struct _ ## T *T #endif - -#ifndef DEFINE_VOID -#define DEFINE_VOID(T) typedef void* T -#endif diff --git a/src/api/z3_optimization.h b/src/api/z3_optimization.h index 750f286a5..18cee9bec 100644 --- a/src/api/z3_optimization.h +++ b/src/api/z3_optimization.h @@ -56,11 +56,23 @@ extern "C" { \brief Assert hard constraint to the optimization context. \sa Z3_optimize_assert_soft + \sa Z3_optimize_assert_and_track def_API('Z3_optimize_assert', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(AST))) */ void Z3_API Z3_optimize_assert(Z3_context c, Z3_optimize o, Z3_ast a); + + /** + \brief Assert tracked hard constraint to the optimization context. + + \sa Z3_optimize_assert + \sa Z3_optimize_assert_soft + + def_API('Z3_optimize_assert_and_track', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(AST), _in(AST))) + */ + void Z3_API Z3_optimize_assert_and_track(Z3_context c, Z3_optimize o, Z3_ast a, Z3_ast t); + /** \brief Assert soft constraint to the optimization context. \param c - context @@ -142,7 +154,7 @@ extern "C" { /** \brief Retrieve a string that describes the last status returned by #Z3_optimize_check. - Use this method when #Z3_optimize_check returns Z3_L_UNDEF. + Use this method when #Z3_optimize_check returns \c Z3_L_UNDEF. def_API('Z3_optimize_get_reason_unknown', STRING, (_in(CONTEXT), _in(OPTIMIZE) )) */ @@ -228,8 +240,8 @@ extern "C" { /** \brief Retrieve lower bound value or approximation for the i'th optimization objective. The returned vector is of length 3. It always contains numerals. - The three numerals are coefficients a, b, c and encode the result of \c Z3_optimize_get_lower - a * infinity + b + c * epsilon. + The three numerals are coefficients \c a, \c b, \c c and encode the result of + #Z3_optimize_get_lower \ccode{a * infinity + b + c * epsilon}. \param c - context \param o - optimization context @@ -330,7 +342,7 @@ extern "C" { /** \brief Return objectives on the optimization context. If the objective function is a max-sat objective it is returned - as a Pseudo-Boolean (minimization) sum of the form (+ (if f1 w1 0) (if f2 w2 0) ...) + as a Pseudo-Boolean (minimization) sum of the form \ccode{(+ (if f1 w1 0) (if f2 w2 0) ...)} If the objective function is entered as a maximization objective, then return the corresponding minimization objective. In this way the resulting objective function is always returned as a minimization objective. diff --git a/src/api/z3_polynomial.h b/src/api/z3_polynomial.h index 0561bfd9c..5f4815d02 100644 --- a/src/api/z3_polynomial.h +++ b/src/api/z3_polynomial.h @@ -36,9 +36,8 @@ extern "C" { \pre \c p, \c q and \c x are Z3 expressions where \c p and \c q are arithmetic terms. Note that, any subterm that cannot be viewed as a polynomial is assumed to be a variable. - Example: f(a) is a considered to be a variable in the polynomial - - f(a)*f(a) + 2*f(a) + 1 + Example: \ccode{f(a)} is a considered to be a variable in the polynomial \ccode{ + f(a)*f(a) + 2*f(a) + 1} def_API('Z3_polynomial_subresultants', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ diff --git a/src/api/z3_rcf.h b/src/api/z3_rcf.h index dc025bddd..4e4ecbd15 100644 --- a/src/api/z3_rcf.h +++ b/src/api/z3_rcf.h @@ -74,7 +74,7 @@ extern "C" { Z3_rcf_num Z3_API Z3_rcf_mk_infinitesimal(Z3_context c); /** - \brief Store in roots the roots of the polynomial a[n-1]*x^{n-1} + ... + a[0]. + \brief Store in roots the roots of the polynomial \ccode{a[n-1]*x^{n-1} + ... + a[0]}. The output vector \c roots must have size \c n. It returns the number of roots of the polynomial. @@ -85,91 +85,91 @@ extern "C" { unsigned Z3_API Z3_rcf_mk_roots(Z3_context c, unsigned n, Z3_rcf_num const a[], Z3_rcf_num roots[]); /** - \brief Return the value a + b. + \brief Return the value \ccode{a + b}. def_API('Z3_rcf_add', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_rcf_num Z3_API Z3_rcf_add(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** - \brief Return the value a - b. + \brief Return the value \ccode{a - b}. def_API('Z3_rcf_sub', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_rcf_num Z3_API Z3_rcf_sub(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** - \brief Return the value a * b. + \brief Return the value \ccode{a * b}. def_API('Z3_rcf_mul', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_rcf_num Z3_API Z3_rcf_mul(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** - \brief Return the value a / b. + \brief Return the value \ccode{a / b}. def_API('Z3_rcf_div', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_rcf_num Z3_API Z3_rcf_div(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** - \brief Return the value -a + \brief Return the value \ccode{-a}. def_API('Z3_rcf_neg', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM))) */ Z3_rcf_num Z3_API Z3_rcf_neg(Z3_context c, Z3_rcf_num a); /** - \brief Return the value 1/a + \brief Return the value \ccode{1/a}. def_API('Z3_rcf_inv', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM))) */ Z3_rcf_num Z3_API Z3_rcf_inv(Z3_context c, Z3_rcf_num a); /** - \brief Return the value a^k + \brief Return the value \ccode{a^k}. def_API('Z3_rcf_power', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) */ Z3_rcf_num Z3_API Z3_rcf_power(Z3_context c, Z3_rcf_num a, unsigned k); /** - \brief Return \c true if a < b + \brief Return \c true if \ccode{a < b}. def_API('Z3_rcf_lt', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ bool Z3_API Z3_rcf_lt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** - \brief Return \c true if a > b + \brief Return \c true if \ccode{a > b}. def_API('Z3_rcf_gt', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ bool Z3_API Z3_rcf_gt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** - \brief Return \c true if a <= b + \brief Return \c true if \ccode{a <= b}. def_API('Z3_rcf_le', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ bool Z3_API Z3_rcf_le(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** - \brief Return \c true if a >= b + \brief Return \c true if \ccode{a >= b}. def_API('Z3_rcf_ge', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ bool Z3_API Z3_rcf_ge(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** - \brief Return \c true if a == b + \brief Return \c true if \ccode{a == b}. def_API('Z3_rcf_eq', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ bool Z3_API Z3_rcf_eq(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** - \brief Return \c true if a != b + \brief Return \c true if \ccode{a != b}. def_API('Z3_rcf_neq', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ @@ -191,7 +191,7 @@ extern "C" { /** \brief Extract the "numerator" and "denominator" of the given RCF numeral. - We have that a = n/d, moreover n and d are not represented using rational functions. + We have that \ccode{a = n/d}, moreover \c n and \c d are not represented using rational functions. def_API('Z3_rcf_get_numerator_denominator', VOID, (_in(CONTEXT), _in(RCF_NUM), _out(RCF_NUM), _out(RCF_NUM))) */ diff --git a/src/api/z3_spacer.h b/src/api/z3_spacer.h index 88129d095..09cbe6a51 100644 --- a/src/api/z3_spacer.h +++ b/src/api/z3_spacer.h @@ -37,9 +37,9 @@ extern "C" { \endcode query returns - - Z3_L_FALSE if the query is unsatisfiable. - - Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer. - - Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed. + - \c Z3_L_FALSE if the query is unsatisfiable. + - \c Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer. + - \c Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed. def_API('Z3_fixedpoint_query_from_lvl', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST), _in(UINT))) */ @@ -48,7 +48,7 @@ extern "C" { /** \brief Retrieve a bottom-up (from query) sequence of ground facts - The previous call to Z3_fixedpoint_query must have returned Z3_L_TRUE. + The previous call to #Z3_fixedpoint_query must have returned \c Z3_L_TRUE. def_API('Z3_fixedpoint_get_ground_sat_answer', AST, (_in(CONTEXT), _in(FIXEDPOINT))) */ diff --git a/src/ast/CMakeLists.txt b/src/ast/CMakeLists.txt index ce2817b58..56ab78b8a 100644 --- a/src/ast/CMakeLists.txt +++ b/src/ast/CMakeLists.txt @@ -17,6 +17,7 @@ z3_add_component(ast csp_decl_plugin.cpp datatype_decl_plugin.cpp decl_collector.cpp + display_dimacs.cpp dl_decl_plugin.cpp expr2polynomial.cpp expr2var.cpp diff --git a/src/ast/act_cache.cpp b/src/ast/act_cache.cpp index 14f0985b4..db3f0f12b 100644 --- a/src/ast/act_cache.cpp +++ b/src/ast/act_cache.cpp @@ -73,11 +73,9 @@ void act_cache::init() { } void act_cache::dec_refs() { - map::iterator it = m_table.begin(); - map::iterator end = m_table.end(); - for (; it != end; ++it) { - m_manager.dec_ref((*it).m_key); - m_manager.dec_ref(UNTAG(expr*, (*it).m_value)); + for (auto & kv : m_table) { + m_manager.dec_ref(kv.m_key.first); + m_manager.dec_ref(UNTAG(expr*, kv.m_value)); } } @@ -105,18 +103,18 @@ act_cache::~act_cache() { void act_cache::del_unused() { unsigned sz = m_queue.size(); while (m_qhead < sz) { - expr * k = m_queue[m_qhead]; + entry_t const& e = m_queue[m_qhead]; m_qhead++; - SASSERT(m_table.contains(k)); - map::key_value * entry = m_table.find_core(k); + SASSERT(m_table.contains(e)); + map::key_value * entry = m_table.find_core(e); SASSERT(entry); if (GET_TAG(entry->m_value) == 0) { // Key k was never accessed by client code. // That is, find(k) was never executed by client code. m_unused--; expr * v = entry->m_value; - m_table.erase(k); - m_manager.dec_ref(k); + m_table.erase(e); + m_manager.dec_ref(e.first); m_manager.dec_ref(v); break; } @@ -135,12 +133,13 @@ void act_cache::del_unused() { /** \brief Insert a new entry k -> v into the cache. */ -void act_cache::insert(expr * k, expr * v) { +void act_cache::insert(expr * k, unsigned offset, expr * v) { SASSERT(k); + entry_t e(k, offset); if (m_unused >= m_max_unused) del_unused(); expr * dummy = reinterpret_cast(1); - map::key_value & entry = m_table.insert_if_not_there(k, dummy); + map::key_value & entry = m_table.insert_if_not_there(e, dummy); #if 0 unsigned static counter = 0; counter++; @@ -156,7 +155,7 @@ void act_cache::insert(expr * k, expr * v) { m_manager.inc_ref(k); m_manager.inc_ref(v); entry.m_value = v; - m_queue.push_back(k); + m_queue.push_back(e); m_unused++; DEBUG_CODE(expected_tag = 0;); // new entry } @@ -175,7 +174,7 @@ void act_cache::insert(expr * k, expr * v) { } DEBUG_CODE({ expr * v2; - SASSERT(m_table.find(k, v2)); + SASSERT(m_table.find(e, v2)); SASSERT(v == UNTAG(expr*, v2)); SASSERT(expected_tag == GET_TAG(v2)); }); @@ -185,8 +184,9 @@ void act_cache::insert(expr * k, expr * v) { \brief Search for key k in the cache. If entry k -> (v, tag) is found, we set tag to 1. */ -expr * act_cache::find(expr * k) { - map::key_value * entry = m_table.find_core(k); +expr * act_cache::find(expr * k, unsigned offset) { + entry_t e(k, offset); + map::key_value * entry = m_table.find_core(e); if (entry == nullptr) return nullptr; if (GET_TAG(entry->m_value) == 0) { @@ -196,7 +196,7 @@ expr * act_cache::find(expr * k) { m_unused--; DEBUG_CODE({ expr * v; - SASSERT(m_table.find(k, v)); + SASSERT(m_table.find(e, v)); SASSERT(GET_TAG(v) == 1); }); } diff --git a/src/ast/act_cache.h b/src/ast/act_cache.h index 67c5cf050..49b49face 100644 --- a/src/ast/act_cache.h +++ b/src/ast/act_cache.h @@ -26,9 +26,15 @@ Notes: class act_cache { ast_manager & m_manager; - typedef cmap, default_eq > map; + typedef std::pair entry_t; + struct entry_hash { + unsigned operator()(entry_t const& e) const { + return e.first->hash() + e.second; + } + }; + typedef cmap > map; map m_table; - ptr_vector m_queue; // recently created queue + svector m_queue; // recently created queue unsigned m_qhead; unsigned m_unused; unsigned m_max_unused; @@ -42,8 +48,10 @@ public: act_cache(ast_manager & m); act_cache(ast_manager & m, unsigned max_unused); ~act_cache(); - void insert(expr * k, expr * v); - expr * find(expr * k); + void insert(expr * k, expr * v) { insert(k, 0, v); } + expr * find(expr * k) { return find(k, 0); } + void insert(expr * k, unsigned offset, expr * v); + expr * find(expr * k, unsigned offset); void reset(); void cleanup(); unsigned size() const { return m_table.size(); } diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp index fa17b591d..44c423c09 100644 --- a/src/ast/array_decl_plugin.cpp +++ b/src/ast/array_decl_plugin.cpp @@ -571,6 +571,11 @@ func_decl * array_recognizers::get_as_array_func_decl(expr * n) const { return to_func_decl(to_app(n)->get_decl()->get_parameter(0).get_ast()); } +func_decl * array_recognizers::get_as_array_func_decl(func_decl * f) const { + SASSERT(is_as_array(f)); + return to_func_decl(f->get_parameter(0).get_ast()); +} + array_util::array_util(ast_manager& m): array_recognizers(m.mk_family_id("array")), m_manager(m) { diff --git a/src/ast/array_decl_plugin.h b/src/ast/array_decl_plugin.h index beaccfbd3..c735fb811 100644 --- a/src/ast/array_decl_plugin.h +++ b/src/ast/array_decl_plugin.h @@ -149,7 +149,9 @@ public: bool is_const(func_decl* f) const { return is_decl_of(f, m_fid, OP_CONST_ARRAY); } bool is_map(func_decl* f) const { return is_decl_of(f, m_fid, OP_ARRAY_MAP); } bool is_as_array(func_decl* f) const { return is_decl_of(f, m_fid, OP_AS_ARRAY); } + bool is_as_array(func_decl* f, func_decl*& g) const { return is_decl_of(f, m_fid, OP_AS_ARRAY) && (g = get_as_array_func_decl(f), true); } func_decl * get_as_array_func_decl(expr * n) const; + func_decl * get_as_array_func_decl(func_decl* f) const; }; class array_util : public array_recognizers { diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 0f6a81a59..b84dac136 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1407,6 +1407,7 @@ ast_manager::ast_manager(ast_manager const & src, bool disable_proofs): m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true); init(); copy_families_plugins(src); + update_fresh_id(src); } void ast_manager::update_fresh_id(ast_manager const& m) { @@ -2156,7 +2157,6 @@ app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const app * new_node = nullptr; unsigned sz = app::get_obj_size(num_args); void * mem = allocate_node(sz); - try { if (m_int_real_coercions && coercion_needed(decl, num_args, args)) { expr_ref_buffer new_args(*this); diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 5f2d36279..ceeab30ff 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -238,7 +238,7 @@ format * smt2_pp_environment::pp_float_literal(app * t, bool use_bv_lits, bool u string_buffer<> buf; VERIFY(get_futil().is_numeral(t, v)); if (fm.is_nan(v)) { - buf << "(_ NaN " << v.get().get_ebits() << " " << v.get().get_sbits() << ")"; + buf << "(_ NaN " << v.get().get_ebits() << " " << v.get().get_sbits() << ")"; return mk_string(m, buf.c_str()); } else if (fm.is_pinf(v)) { diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index 0def08094..2490e0314 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -399,12 +399,12 @@ class smt_printer { pp_marked_expr(n->get_arg(0)); m_out << ") (_ bv1 1))"; } - else if (m_manager.is_label(n, pos, names) && names.size() >= 1) { + else if (m_manager.is_label(n, pos, names) && !names.empty()) { m_out << "(! "; pp_marked_expr(n->get_arg(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) { + else if (m_manager.is_label_lit(n, names) && !names.empty()) { m_out << "(! true :lblpos " << m_renaming.get_symbol(names[0], false) << ")"; } else if (num_args == 0) { @@ -952,7 +952,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { if (m_logic != symbol::null && m_logic != symbol("")) { strm << "(set-logic " << m_logic << ")\n"; } - if (m_attributes.size() > 0) { + if (!m_attributes.empty()) { strm << "; " << m_attributes.c_str(); } diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index c4bada552..50f1fbfed 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -558,11 +558,15 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p case OP_ROTATE_LEFT: if (arity != 1) m_manager->raise_exception("rotate left expects one argument"); + if (num_parameters != 1 || !parameters[0].is_int()) + m_manager->raise_exception("rotate left expects one integer parameter"); return m_manager->mk_func_decl(m_rotate_left_sym, arity, domain, domain[0], func_decl_info(m_family_id, k, num_parameters, parameters)); case OP_ROTATE_RIGHT: if (arity != 1) m_manager->raise_exception("rotate right expects one argument"); + if (num_parameters != 1 || !parameters[0].is_int()) + m_manager->raise_exception("rotate right expects one integer parameter"); return m_manager->mk_func_decl(m_rotate_right_sym, arity, domain, domain[0], func_decl_info(m_family_id, k, num_parameters, parameters)); case OP_REPEAT: diff --git a/src/ast/csp_decl_plugin.h b/src/ast/csp_decl_plugin.h index 7c10bbfe9..486aa7f03 100644 --- a/src/ast/csp_decl_plugin.h +++ b/src/ast/csp_decl_plugin.h @@ -148,7 +148,7 @@ public: bool is_resource(expr* e, unsigned& r); bool is_makespan(expr* e, unsigned& r); bool is_add_resource_available(expr * e, expr *& res, unsigned& loadpct, unsigned& cap_time, uint64_t& start, uint64_t& end, svector& properites); - bool is_add_job_resource(expr * e, expr *& job, expr*& res, unsigned& loadpct, uint64_t& capacity, uint64_t& end, svector& properites); + bool is_add_job_resource(expr * e, expr *& job, expr*& res, unsigned& loadpct, uint64_t& capacity, uint64_t& finite_capacity_end, svector& properites); bool is_set_preemptable(expr* e, expr *& job); bool is_model(expr* e) const { return is_app_of(e, m_fid, OP_JS_MODEL); } bool is_js_properties(expr* e, svector& properties); diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp index db3c0e2a0..8b57a33ae 100644 --- a/src/ast/datatype_decl_plugin.cpp +++ b/src/ast/datatype_decl_plugin.cpp @@ -143,7 +143,77 @@ namespace datatype { } return r; } - size* size::mk_power(size* a1, size* a2) { return alloc(power, a1, a2); } + + size* size::mk_power(size* a1, size* a2) { + return alloc(power, a1, a2); + } + + + sort_size plus::eval(obj_map const& S) { + rational r(0); + ptr_vector todo; + todo.push_back(m_arg1); + todo.push_back(m_arg2); + while (!todo.empty()) { + size* s = todo.back(); + todo.pop_back(); + plus* p = dynamic_cast(s); + if (p) { + todo.push_back(p->m_arg1); + todo.push_back(p->m_arg2); + } + else { + sort_size sz = s->eval(S); + if (sz.is_infinite()) return sz; + if (sz.is_very_big()) return sz; + r += rational(sz.size(), rational::ui64()); + } + } + return sort_size(r); + } + + size* plus::subst(obj_map& S) { + return mk_plus(m_arg1->subst(S), m_arg2->subst(S)); + } + + sort_size times::eval(obj_map const& S) { + sort_size s1 = m_arg1->eval(S); + sort_size s2 = m_arg2->eval(S); + if (s1.is_infinite()) return s1; + if (s2.is_infinite()) return s2; + if (s1.is_very_big()) return s1; + if (s2.is_very_big()) return s2; + rational r = rational(s1.size(), rational::ui64()) * rational(s2.size(), rational::ui64()); + return sort_size(r); + } + + size* times::subst(obj_map& S) { + return mk_times(m_arg1->subst(S), m_arg2->subst(S)); + } + + sort_size power::eval(obj_map const& S) { + sort_size s1 = m_arg1->eval(S); + sort_size s2 = m_arg2->eval(S); + // s1^s2 + if (s1.is_infinite()) return s1; + if (s2.is_infinite()) return s2; + if (s1.is_very_big()) return s1; + if (s2.is_very_big()) return s2; + if (s1.size() == 1) return s1; + if (s2.size() == 1) return s1; + if (s1.size() > (2 << 20) || s2.size() > 10) return sort_size::mk_very_big(); + rational r = ::power(rational(s1.size(), rational::ui64()), static_cast(s2.size())); + return sort_size(r); + } + + size* power::subst(obj_map& S) { + return mk_power(m_arg1->subst(S), m_arg2->subst(S)); + } + + size* sparam::subst(obj_map& S) { + return S[m_param]; + } + } namespace decl { @@ -625,13 +695,14 @@ namespace datatype { param_size::size* sz; obj_map S; unsigned n = get_datatype_num_parameter_sorts(s); + def & d = get_def(s->get_name()); + SASSERT(n == d.params().size()); for (unsigned i = 0; i < n; ++i) { sort* ps = get_datatype_parameter_sort(s, i); sz = get_sort_size(params, ps); - sz->inc_ref(); - S.insert(ps, sz); - } - def & d = get_def(s->get_name()); + sz->inc_ref(); + S.insert(d.params().get(i), sz); + } sz = d.sort_size()->subst(S); for (auto & kv : S) { kv.m_value->dec_ref(); @@ -708,7 +779,7 @@ namespace datatype { continue; } - ptr_vector s_add; + ptr_vector s_add; for (constructor const* c : d) { ptr_vector s_mul; for (accessor const* a : *c) { @@ -723,7 +794,7 @@ namespace datatype { /** \brief Return true if the inductive datatype is well-founded. - Pre-condition: The given argument constains the parameters of an inductive datatype. + Pre-condition: The given argument constrains the parameters of an inductive datatype. */ bool util::is_well_founded(unsigned num_types, sort* const* sorts) { buffer well_founded(num_types, false); diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h index 736cc0875..cf0b47784 100644 --- a/src/ast/datatype_decl_plugin.h +++ b/src/ast/datatype_decl_plugin.h @@ -132,71 +132,28 @@ namespace datatype { size* m_arg1, *m_arg2; plus(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref();} ~plus() override { m_arg1->dec_ref(); m_arg2->dec_ref(); } - size* subst(obj_map& S) override { return mk_plus(m_arg1->subst(S), m_arg2->subst(S)); } - sort_size eval(obj_map const& S) override { - rational r(0); - ptr_vector todo; - todo.push_back(m_arg1); - todo.push_back(m_arg2); - while (!todo.empty()) { - size* s = todo.back(); - todo.pop_back(); - plus* p = dynamic_cast(s); - if (p) { - todo.push_back(p->m_arg1); - todo.push_back(p->m_arg2); - } - else { - sort_size sz = s->eval(S); - if (sz.is_infinite()) return sz; - if (sz.is_very_big()) return sz; - r += rational(sz.size(), rational::ui64()); - } - } - return sort_size(r); - } + size* subst(obj_map& S) override; + sort_size eval(obj_map const& S) override; }; struct times : public size { size* m_arg1, *m_arg2; times(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref(); } ~times() override { m_arg1->dec_ref(); m_arg2->dec_ref(); } - size* subst(obj_map& S) override { return mk_times(m_arg1->subst(S), m_arg2->subst(S)); } - sort_size eval(obj_map const& S) override { - sort_size s1 = m_arg1->eval(S); - sort_size s2 = m_arg2->eval(S); - if (s1.is_infinite()) return s1; - if (s2.is_infinite()) return s2; - if (s1.is_very_big()) return s1; - if (s2.is_very_big()) return s2; - rational r = rational(s1.size(), rational::ui64()) * rational(s2.size(), rational::ui64()); - return sort_size(r); - } + size* subst(obj_map& S) override; + sort_size eval(obj_map const& S) override; }; struct power : public size { size* m_arg1, *m_arg2; power(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref(); } ~power() override { m_arg1->dec_ref(); m_arg2->dec_ref(); } - size* subst(obj_map& S) override { return mk_power(m_arg1->subst(S), m_arg2->subst(S)); } - sort_size eval(obj_map const& S) override { - sort_size s1 = m_arg1->eval(S); - sort_size s2 = m_arg2->eval(S); - // s1^s2 - if (s1.is_infinite()) return s1; - if (s2.is_infinite()) return s2; - if (s1.is_very_big()) return s1; - if (s2.is_very_big()) return s2; - if (s1.size() == 1) return s1; - if (s2.size() == 1) return s1; - if (s1.size() > (2 << 20) || s2.size() > 10) return sort_size::mk_very_big(); - rational r = ::power(rational(s1.size(), rational::ui64()), static_cast(s2.size())); - return sort_size(r); - } + size* subst(obj_map& S) override; + sort_size eval(obj_map const& S) override; }; struct sparam : public size { sort_ref m_param; sparam(sort_ref& p): m_param(p) {} ~sparam() override {} - size* subst(obj_map& S) override { return S[m_param]; } + size* subst(obj_map& S) override; sort_size eval(obj_map const& S) override { return S[m_param]; } }; }; diff --git a/src/ast/display_dimacs.cpp b/src/ast/display_dimacs.cpp new file mode 100644 index 000000000..da39538d9 --- /dev/null +++ b/src/ast/display_dimacs.cpp @@ -0,0 +1,81 @@ +/*++ +Copyright (c) 2019 Microsoft Corporation + +Module Name: + + display_dimacs.h + +Abstract: + + Display expressions in DIMACS format. + +Author: + + Nikolaj Bjorner (nbjorner0 2019-01-24 + +Revision History: + +--*/ + +#include "ast.h" +#include "display_dimacs.h" + +std::ostream& display_dimacs(std::ostream& out, expr_ref_vector const& fmls) { + ast_manager& m = fmls.m(); + unsigned_vector expr2var; + ptr_vector exprs; + unsigned num_vars = 0; + unsigned num_cls = fmls.size(); + for (expr * f : fmls) { + unsigned num_lits; + expr * const * lits; + if (m.is_or(f)) { + num_lits = to_app(f)->get_num_args(); + lits = to_app(f)->get_args(); + } + else { + num_lits = 1; + lits = &f; + } + for (unsigned j = 0; j < num_lits; j++) { + expr * l = lits[j]; + if (m.is_not(l)) + l = to_app(l)->get_arg(0); + if (expr2var.get(l->get_id(), UINT_MAX) == UINT_MAX) { + num_vars++; + expr2var.setx(l->get_id(), num_vars, UINT_MAX); + exprs.setx(l->get_id(), l, nullptr); + } + } + } + out << "p cnf " << num_vars << " " << num_cls << "\n"; + for (expr* f : fmls) { + unsigned num_lits; + expr * const * lits; + if (m.is_or(f)) { + num_lits = to_app(f)->get_num_args(); + lits = to_app(f)->get_args(); + } + else { + num_lits = 1; + lits = &f; + } + for (unsigned j = 0; j < num_lits; j++) { + expr * l = lits[j]; + if (m.is_not(l)) { + out << "-"; + l = to_app(l)->get_arg(0); + } + SASSERT(exprs[l->get_id()]); + out << expr2var[l->get_id()] << " "; + } + out << "0\n"; + } + for (expr* e : exprs) { + if (e && is_app(e)) { + symbol const& n = to_app(e)->get_decl()->get_name(); + out << "c " << expr2var[e->get_id()] << " " << n << "\n"; + } + } + return out; +} diff --git a/src/ast/display_dimacs.h b/src/ast/display_dimacs.h new file mode 100644 index 000000000..91c2386be --- /dev/null +++ b/src/ast/display_dimacs.h @@ -0,0 +1,26 @@ +/*++ +Copyright (c) 2019 Microsoft Corporation + +Module Name: + + display_dimacs.h + +Abstract: + + Display expressions in DIMACS format. + +Author: + + Nikolaj Bjorner (nbjorner0 2019-01-24 + +Revision History: + +--*/ +#ifndef DISPLAY_DIMACS_H_ +#define DISPLAY_DIMACS_H_ + +#include "ast.h" + +std::ostream& display_dimacs(std::ostream& out, expr_ref_vector const& fmls); + +#endif /* DISPLAY_DIMACS_H__ */ diff --git a/src/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index f4a538abd..01b06518e 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -147,7 +147,7 @@ namespace datalog { for (unsigned i = 0; i < n; ++i) { parameter const& p = r->get_parameter(i); if (!p.is_ast() || !is_sort(p.get_ast())) { - m_manager->raise_exception("exptected sort parameter"); + m_manager->raise_exception("expected sort parameter"); return false; } sorts.push_back(to_sort(p.get_ast())); @@ -185,7 +185,7 @@ namespace datalog { verbose_stream() << "Domain: " << mk_pp(domain[0], m) << "\n" << mk_pp(sorts[i], m) << "\n" << mk_pp(domain[i+1], m) << "\n";); - m_manager->raise_exception("sort miss-match for relational access"); + m_manager->raise_exception("sort mismatch for relational access"); return nullptr; } } @@ -252,7 +252,7 @@ namespace datalog { func_decl * dl_decl_plugin::mk_unionw(decl_kind k, sort* s1, sort* s2) { ast_manager& m = *m_manager; if (s1 != s2) { - m_manager->raise_exception("sort miss-match for arguments to union"); + m_manager->raise_exception("sort mismatch for arguments to union"); return nullptr; } if (!is_rel_sort(s1)) { @@ -298,7 +298,7 @@ namespace datalog { return nullptr; } if (sorts[idx] != m.get_sort(e)) { - m_manager->raise_exception("sort miss-match in filter"); + m_manager->raise_exception("sort mismatch in filter"); return nullptr; } break; @@ -391,7 +391,7 @@ namespace datalog { return nullptr; } if (sorts1[i1] != sorts2[i2]) { - m_manager->raise_exception("sort miss-match in join"); + m_manager->raise_exception("sort mismatch in join"); return nullptr; } } @@ -435,7 +435,7 @@ namespace datalog { return nullptr; } if (sorts1[i1] != sorts2[i2]) { - m_manager->raise_exception("sort miss-match in join"); + m_manager->raise_exception("sort mismatch in join"); return nullptr; } } diff --git a/src/ast/expr2polynomial.cpp b/src/ast/expr2polynomial.cpp index 280d7487a..82bace428 100644 --- a/src/ast/expr2polynomial.cpp +++ b/src/ast/expr2polynomial.cpp @@ -436,7 +436,7 @@ struct expr2polynomial::imp { margs.push_back(t); } } - if (margs.size() == 0) { + if (margs.empty()) { args.push_back(m_autil.mk_numeral(rational(1), is_int)); } else if (margs.size() == 1) { @@ -447,7 +447,7 @@ struct expr2polynomial::imp { } } - if (args.size() == 0) { + if (args.empty()) { r = m_autil.mk_numeral(rational(0), is_int); } else if (args.size() == 1) { diff --git a/src/ast/expr2var.cpp b/src/ast/expr2var.cpp index b1fdba2b5..61adcfc3a 100644 --- a/src/ast/expr2var.cpp +++ b/src/ast/expr2var.cpp @@ -29,8 +29,17 @@ void expr2var::insert(expr * n, var v) { TRACE("expr2var", tout << "interpreted:\n" << mk_ismt2_pp(n, m()) << "\n";); m_interpreted_vars = true; } - m().inc_ref(n); - m_mapping.insert(n, v); + unsigned idx = m_id2map.get(n->get_id(), UINT_MAX); + if (idx == UINT_MAX) { + m().inc_ref(n); + idx = m_mapping.size(); + m_mapping.push_back(key_value(n, v)); + m_id2map.setx(n->get_id(), idx, UINT_MAX); + } + else { + m_mapping[idx] = key_value(n, v); + } + m_recent_exprs.push_back(n); } @@ -40,20 +49,22 @@ expr2var::expr2var(ast_manager & m): } expr2var::~expr2var() { - dec_ref_map_keys(m(), m_mapping); + for (auto & kv : m_mapping) { + m().dec_ref(kv.m_key); + } } expr2var::var expr2var::to_var(expr * n) const { - var v = UINT_MAX; - m_mapping.find(n, v); + var v = m_id2map.get(n->get_id(), UINT_MAX); + if (v != UINT_MAX) { + v = m_mapping[v].m_value; + } return v; } void expr2var::display(std::ostream & out) const { - obj_map::iterator it = m_mapping.begin(); - obj_map::iterator end = m_mapping.end(); - for (; it != end; ++it) { - out << mk_ismt2_pp(it->m_key, m()) << " -> " << it->m_value << "\n"; + for (auto const& kv : m_mapping) { + out << mk_ismt2_pp(kv.m_key, m()) << " -> " << kv.m_value << "\n"; } } @@ -68,8 +79,11 @@ void expr2var::mk_inv(expr_ref_vector & var2expr) const { } void expr2var::reset() { - dec_ref_map_keys(m(), m_mapping); - SASSERT(m_mapping.empty()); + for (auto & kv : m_mapping) { + m().dec_ref(kv.m_key); + } + m_mapping.reset(); + m_id2map.reset(); m_recent_exprs.reset(); m_recent_lim.reset(); m_interpreted_vars = false; @@ -83,8 +97,15 @@ void expr2var::pop(unsigned num_scopes) { if (num_scopes > 0) { unsigned sz = m_recent_lim[m_recent_lim.size() - num_scopes]; for (unsigned i = sz; i < m_recent_exprs.size(); ++i) { - m_mapping.erase(m_recent_exprs[i]); - m().dec_ref(m_recent_exprs[i]); + expr* n = m_recent_exprs[i]; + unsigned idx = m_id2map[n->get_id()]; + if (idx + 1 != m_mapping.size()) { + m_id2map[m_mapping.back().m_key->get_id()] = idx; + m_mapping[idx] = m_mapping.back(); + } + m_id2map[n->get_id()] = UINT_MAX; + m_mapping.pop_back(); + m().dec_ref(n); } m_recent_exprs.shrink(sz); m_recent_lim.shrink(m_recent_lim.size() - num_scopes); diff --git a/src/ast/expr2var.h b/src/ast/expr2var.h index 2b4d8c3fe..2bf2fe160 100644 --- a/src/ast/expr2var.h +++ b/src/ast/expr2var.h @@ -32,12 +32,14 @@ Notes: class expr2var { public: typedef unsigned var; - typedef obj_map expr2var_mapping; - typedef expr2var_mapping::iterator iterator; + typedef obj_map::key_data key_value; + typedef key_value const* iterator; typedef ptr_vector::const_iterator recent_iterator; protected: ast_manager & m_manager; - expr2var_mapping m_mapping; + + unsigned_vector m_id2map; + svector m_mapping; ptr_vector m_recent_exprs; unsigned_vector m_recent_lim; bool m_interpreted_vars; @@ -51,7 +53,7 @@ public: var to_var(expr * n) const; - bool is_var(expr * n) const { return m_mapping.contains(n); } + bool is_var(expr * n) const { return m_id2map.get(n->get_id(), UINT_MAX) != UINT_MAX; } void display(std::ostream & out) const; diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 1dc13ff9e..eef2452db 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -3300,11 +3300,11 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args ll = m_bv_util.mk_sign_extend(3, m_bv_util.mk_concat(bv1, m_bv_util.mk_numeral(0, bv_sz-1))); ul = m_bv_util.mk_zero_extend(4, m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, bv_sz-1))); ovfl = m.mk_or(ovfl, m_bv_util.mk_sle(pre_rounded, m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, bv_sz + 3)))); + pre_rounded = m.mk_ite(x_is_neg, m_bv_util.mk_bv_neg(pre_rounded), pre_rounded); in_range = m.mk_and(m.mk_not(ovfl), m_bv_util.mk_sle(ll, pre_rounded), m_bv_util.mk_sle(pre_rounded, ul)); dbg_decouple("fpa2bv_to_bv_in_range_ll", ll); - pre_rounded = m.mk_ite(x_is_neg, m_bv_util.mk_bv_neg(pre_rounded), pre_rounded); } dbg_decouple("fpa2bv_to_bv_in_range_ovfl", ovfl); dbg_decouple("fpa2bv_to_bv_in_range_ul", ul); diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index cf3c8bc31..93b269f87 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -594,7 +594,6 @@ bool pattern_inference_cfg::reduce_quantifier( unsigned new_weight; if (m_database.match_quantifier(q, new_patterns, new_weight)) { DEBUG_CODE(for (unsigned i = 0; i < new_patterns.size(); i++) { SASSERT(is_well_sorted(m, new_patterns.get(i))); }); - quantifier_ref new_q(m); if (q->get_num_patterns() > 0) { // just update the weight... TRACE("pattern_inference", tout << "updating weight to: " << new_weight << "\n" << mk_pp(q, m) << "\n";); @@ -604,10 +603,10 @@ bool pattern_inference_cfg::reduce_quantifier( quantifier_ref tmp(m); tmp = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), q->get_expr()); 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";); + TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(result, m) << "\n";); } if (m.proofs_enabled()) - result_pr = m.mk_rewrite(q, new_q); + result_pr = m.mk_rewrite(q, result); return true; } } diff --git a/src/ast/proofs/proof_utils.cpp b/src/ast/proofs/proof_utils.cpp index 5483a9ea0..5c37c3794 100644 --- a/src/ast/proofs/proof_utils.cpp +++ b/src/ast/proofs/proof_utils.cpp @@ -238,7 +238,7 @@ class reduce_hypotheses { { args.push_back(fact); } - if (args.size() == 0) { return pf; } + if (args.empty()) { return pf; } else if (args.size() == 1) { lemma = args.get(0); } else { diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index 1ac44067f..ebdf86ca5 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -68,7 +68,7 @@ namespace recfun { struct ite_find_p : public i_expr_pred { ast_manager & m; ite_find_p(ast_manager & m) : m(m) {} - virtual bool operator()(expr * e) { return m.is_ite(e); } + bool operator()(expr * e) override { return m.is_ite(e); } }; // ignore ites under quantifiers. // this is redundant as the code @@ -331,7 +331,7 @@ namespace recfun { struct is_imm_pred : is_immediate_pred { util & u; is_imm_pred(util & u) : u(u) {} - bool operator()(expr * rhs) { + bool operator()(expr * rhs) override { // find an `app` that is an application of a defined function struct find : public i_expr_pred { util & u; diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index 0247335e8..b294cdfce 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -56,7 +56,7 @@ namespace recfun { friend class def; func_decl_ref m_pred; //& args) { 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 cf7e7c951..a3bb3b6e1 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h @@ -75,9 +75,23 @@ bool bit_blaster_tpl::is_minus_one(unsigned sz, expr * const * bits) const static void _num2bits(ast_manager & m, rational const & v, unsigned sz, expr_ref_vector & out_bits) { SASSERT(v.is_nonneg()); rational aux = v; - rational two(2); + rational two(2), base32(1ull << 32ull, rational::ui64()); for (unsigned i = 0; i < sz; i++) { - if ((aux % two).is_zero()) + if (i + 32 < sz) { + unsigned u = (aux % base32).get_unsigned(); + for (unsigned j = 0; j < 32; ++j) { + if (0 != (u & (1 << j))) { + out_bits.push_back(m.mk_true()); + } + else { + out_bits.push_back(m.mk_false()); + } + } + aux = div(aux, base32); + i += 31; + continue; + } + else if ((aux % two).is_zero()) out_bits.push_back(m.mk_false()); else out_bits.push_back(m.mk_true()); diff --git a/src/ast/rewriter/bv_bounds.cpp b/src/ast/rewriter/bv_bounds.cpp index f30df7890..1658833a8 100644 --- a/src/ast/rewriter/bv_bounds.cpp +++ b/src/ast/rewriter/bv_bounds.cpp @@ -628,7 +628,7 @@ bool bv_bounds::is_sat_core(app * v) { numeral new_hi = lower - one; numeral ptr = lower; if (has_neg_intervals) { - SASSERT(negative_intervals != NULL); + SASSERT(negative_intervals != nullptr); std::sort(negative_intervals->begin(), negative_intervals->end(), interval_comp); intervals::const_iterator e = negative_intervals->end(); for (intervals::const_iterator i = negative_intervals->begin(); i != e; ++i) { diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 81226b010..f6b760f9c 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -196,6 +196,9 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons return mk_bv_comp(args[0], args[1], result); case OP_MKBV: return mk_mkbv(num_args, args, result); + case OP_BIT2BOOL: + SASSERT(num_args == 1); + return mk_bit2bool(args[0], f->get_parameter(0).get_int(), result); case OP_BSMUL_NO_OVFL: return mk_bvsmul_no_overflow(num_args, args, result); case OP_BUMUL_NO_OVFL: @@ -779,10 +782,11 @@ br_status bv_rewriter::mk_extract(unsigned high, unsigned low, expr * arg, expr_ } } - if (m().is_ite(arg)) { - result = m().mk_ite(to_app(arg)->get_arg(0), - m_mk_extract(high, low, to_app(arg)->get_arg(1)), - m_mk_extract(high, low, to_app(arg)->get_arg(2))); + expr* c = nullptr, *t = nullptr, *e = nullptr; + if (m().is_ite(arg, c, t, e) && + (t->get_ref_count() == 1 || !m().is_ite(t)) && + (e->get_ref_count() == 1 || !m().is_ite(e))) { + result = m().mk_ite(c, m_mk_extract(high, low, t), m_mk_extract(high, low, e)); return BR_REWRITE2; } @@ -2202,6 +2206,19 @@ br_status bv_rewriter::mk_bv_mul(unsigned num_args, expr * const * args, expr_re return st; } +br_status bv_rewriter::mk_bit2bool(expr * n, int idx, expr_ref & result) { + rational v, bit; + unsigned sz = 0; + if (!is_numeral(n, v, sz)) + return BR_FAILED; + if (idx < 0 || idx >= static_cast(sz)) + return BR_FAILED; + div(v, rational::power_of_two(idx), bit); + mod(bit, rational(2), bit); + result = m().mk_bool_val(bit.is_one()); + return BR_DONE; +} + br_status bv_rewriter::mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result) { unsigned sz = get_bv_size(lhs); if (sz != 1) diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index 205ebbf8e..8ad589a49 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -134,6 +134,7 @@ class bv_rewriter : public poly_rewriter { br_status mk_bv_redand(expr * arg, expr_ref & result); br_status mk_bv_comp(expr * arg1, expr * arg2, expr_ref & result); br_status mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result); + br_status mk_bit2bool(expr * lhs, int idx, expr_ref & result); br_status mk_blast_eq_value(expr * lhs, expr * rhs, expr_ref & result); br_status mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result); br_status mk_mkbv(unsigned num, expr * const * args, expr_ref & result); diff --git a/src/ast/rewriter/bv_trailing.cpp b/src/ast/rewriter/bv_trailing.cpp index 433ef6f66..f7c9e121e 100644 --- a/src/ast/rewriter/bv_trailing.cpp +++ b/src/ast/rewriter/bv_trailing.cpp @@ -364,7 +364,7 @@ struct bv_trailing::imp { } void reset_cache(const unsigned condition) { - SASSERT(m_count_cache[0] == NULL); + SASSERT(m_count_cache[0] == nullptr); for (unsigned i = 1; i <= TRAILING_DEPTH; ++i) { if (m_count_cache[i] == nullptr) continue; TRACE("bv-trailing", tout << "may reset cache " << i << " " << condition << "\n";); diff --git a/src/ast/rewriter/der.cpp b/src/ast/rewriter/der.cpp index 027846c31..c95c07f63 100644 --- a/src/ast/rewriter/der.cpp +++ b/src/ast/rewriter/der.cpp @@ -374,13 +374,11 @@ void der::apply_substitution(quantifier * q, expr_ref & r) { expr_ref_buffer new_patterns(m_manager); expr_ref_buffer new_no_patterns(m_manager); for (unsigned j = 0; j < q->get_num_patterns(); j++) { - expr_ref new_pat = m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr()); - new_patterns.push_back(new_pat); + new_patterns.push_back(m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr())); } for (unsigned j = 0; j < q->get_num_no_patterns(); j++) { - expr_ref new_nopat = m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr()); - new_no_patterns.push_back(new_nopat); + new_no_patterns.push_back(m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr())); } r = m_manager.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), diff --git a/src/ast/rewriter/distribute_forall.cpp b/src/ast/rewriter/distribute_forall.cpp index 54d7e2eca..c621579cb 100644 --- a/src/ast/rewriter/distribute_forall.cpp +++ b/src/ast/rewriter/distribute_forall.cpp @@ -126,8 +126,7 @@ void distribute_forall::reduce1_quantifier(quantifier * q) { br.mk_not(arg, not_arg); quantifier_ref tmp_q(m_manager); tmp_q = m_manager.update_quantifier(q, not_arg); - expr_ref new_q = elim_unused_vars(m_manager, tmp_q, params_ref()); - new_args.push_back(new_q); + new_args.push_back(elim_unused_vars(m_manager, tmp_q, params_ref())); } expr_ref result(m_manager); // m_bsimp.mk_and actually constructs a (not (or ...)) formula, diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp new file mode 100644 index 000000000..31909f12b --- /dev/null +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -0,0 +1,201 @@ +/*++ +Copyright (c) 2019 Microsoft Corporation + +Module Name: + + hoist_rewriter.cpp + +Abstract: + + Hoist predicates over disjunctions + +Author: + + Nikolaj Bjorner (nbjorner) 2019-2-4 + +Notes: + +--*/ + + +#include "ast/rewriter/hoist_rewriter.h" +#include "ast/ast_util.h" +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/ast_pp.h" + + +hoist_rewriter::hoist_rewriter(ast_manager & m, params_ref const & p): + m_manager(m), m_args1(m), m_args2(m) { + updt_params(p); +} + +br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & result) { + if (num_args < 2) { + return BR_FAILED; + } + for (unsigned i = 0; i < num_args; ++i) { + if (!is_and(es[i], nullptr)) { + return BR_FAILED; + } + } + + bool turn = false; + m_preds1.reset(); + m_preds2.reset(); + m_uf1.reset(); + m_uf2.reset(); + m_expr2var.reset(); + m_var2expr.reset(); + basic_union_find* uf[2] = { &m_uf1, &m_uf2 }; + obj_hashtable* preds[2] = { &m_preds1, &m_preds2 }; + expr_ref_vector* args[2] = { &m_args1, &m_args2 }; + VERIFY(is_and(es[0], args[turn])); + expr* e1, *e2; + for (expr* e : *(args[turn])) { + if (m().is_eq(e, e1, e2)) { + (*uf)[turn].merge(mk_var(e1), mk_var(e2)); + } + else { + (*preds)[turn].insert(e); + } + } + unsigned round = 0; + for (unsigned j = 1; j < num_args; ++j) { + ++round; + m_es.reset(); + m_mark.reset(); + + bool last = turn; + turn = !turn; + (*preds)[turn].reset(); + reset(m_uf0); + VERIFY(is_and(es[j], args[turn])); + + for (expr* e : *args[turn]) { + if (m().is_eq(e, e1, e2)) { + m_es.push_back(e1); + m_uf0.merge(mk_var(e1), mk_var(e2)); + } + else if ((*preds)[last].contains(e)) { + (*preds)[turn].insert(e); + } + } + + if ((*preds)[turn].empty() && m_es.empty()) { + return BR_FAILED; + } + + m_eqs.reset(); + for (expr* e : m_es) { + if (m_mark.is_marked(e)) { + continue; + } + unsigned u = mk_var(e); + unsigned v = u; + m_roots.reset(); + do { + m_mark.mark(e); + unsigned r = (*uf)[last].find(v); + if (m_roots.find(r, e2)) { + m_eqs.push_back(std::make_pair(e, e2)); + } + else { + m_roots.insert(r, e); + } + v = m_uf0.next(v); + e = mk_expr(v); + } + while (u != v); + } + reset((*uf)[turn]); + for (auto const& p : m_eqs) { + (*uf)[turn].merge(mk_var(p.first), mk_var(p.second)); + } + if ((*preds)[turn].empty() && m_eqs.empty()) { + return BR_FAILED; + } + } + // p & eqs & (or fmls) + expr_ref_vector fmls(m()), ors(m()); + expr_safe_replace subst(m()); + for (expr * p : (*preds)[turn]) { + expr* q = nullptr; + if (m().is_not(p, q)) { + subst.insert(q, m().mk_false()); + } + else { + subst.insert(p, m().mk_true()); + } + fmls.push_back(p); + } + for (auto const& p : m_eqs) { + subst.insert(p.first, p.second); + fmls.push_back(m().mk_eq(p.first, p.second)); + } + + for (unsigned i = 0; i < num_args; ++i) { + expr_ref tmp(m()); + subst(es[i], tmp); + ors.push_back(tmp); + } + fmls.push_back(m().mk_or(ors.size(), ors.c_ptr())); + result = m().mk_and(fmls.size(), fmls.c_ptr()); + TRACE("hoist", + for (unsigned i = 0; i < num_args; ++i) { + tout << mk_pp(es[i], m()) << "\n"; + } + tout << "=>\n"; + tout << result << "\n";); + return BR_DONE; +} + +unsigned hoist_rewriter::mk_var(expr* e) { + unsigned v = 0; + if (m_expr2var.find(e, v)) { + return v; + } + v = m_uf1.mk_var(); + v = m_uf2.mk_var(); + SASSERT(v == m_var2expr.size()); + m_expr2var.insert(e, v); + m_var2expr.push_back(e); + return v; +} + +br_status hoist_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { + switch (f->get_decl_kind()) { + case OP_OR: + return mk_or(num_args, args, result); + default: + return BR_FAILED; + } +} + +bool hoist_rewriter::is_and(expr * e, expr_ref_vector* args) { + if (m().is_and(e)) { + if (args) { + args->reset(); + args->append(to_app(e)->get_num_args(), to_app(e)->get_args()); + } + return true; + } + if (m().is_not(e, e) && m().is_or(e)) { + if (args) { + args->reset(); + for (expr* arg : *to_app(e)) { + args->push_back(::mk_not(m(), arg)); + } + } + return true; + } + return false; +} + + +void hoist_rewriter::reset(basic_union_find& uf) { + uf.reset(); + for (expr* e : m_var2expr) { + (void)e; + uf.mk_var(); + } +} diff --git a/src/ast/rewriter/hoist_rewriter.h b/src/ast/rewriter/hoist_rewriter.h new file mode 100644 index 000000000..0f45a073c --- /dev/null +++ b/src/ast/rewriter/hoist_rewriter.h @@ -0,0 +1,80 @@ +/*++ +Copyright (c) 2019 Microsoft Corporation + +Module Name: + + hoist_rewriter.h + +Abstract: + + Hoist predicates over disjunctions + +Author: + + Nikolaj Bjorner (nbjorner) 2019-2-4 + +Notes: + +--*/ +#ifndef HOIST_REWRITER_H_ +#define HOIST_REWRITER_H_ + +#include "ast/ast.h" +#include "ast/rewriter/rewriter.h" +#include "util/params.h" +#include "util/union_find.h" +#include "util/obj_hashtable.h" + +class hoist_rewriter { + ast_manager & m_manager; + expr_ref_vector m_args1, m_args2; + obj_hashtable m_preds1, m_preds2; + basic_union_find m_uf1, m_uf2, m_uf0; + ptr_vector m_es; + svector> m_eqs; + u_map m_roots; + obj_map m_expr2var; + ptr_vector m_var2expr; + expr_mark m_mark; + + br_status mk_or(unsigned num_args, expr * const * args, expr_ref & result); + + bool is_and(expr* e, expr_ref_vector* args); + + bool is_var(expr* e) { return m_expr2var.contains(e); } + expr* mk_expr(unsigned v) { return m_var2expr[v]; } + unsigned mk_var(expr* e); + + void reset(basic_union_find& uf); + +public: + hoist_rewriter(ast_manager & m, params_ref const & p = params_ref()); + ast_manager& m() const { return m_manager; } + family_id get_fid() const { return m().get_basic_family_id(); } + bool is_eq(expr * t) const { return m().is_eq(t); } + void updt_params(params_ref const & p) {} + static void get_param_descrs(param_descrs & r) {} + br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); +}; + +struct hoist_rewriter_cfg : public default_rewriter_cfg { + hoist_rewriter 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) { + result_pr = nullptr; + if (f->get_family_id() != m_r.get_fid()) + return BR_FAILED; + return m_r.mk_app_core(f, num, args, result); + } + hoist_rewriter_cfg(ast_manager & m, params_ref const & p):m_r(m, p) {} +}; + +class hoist_rewriter_star : public rewriter_tpl { + hoist_rewriter_cfg m_cfg; +public: + hoist_rewriter_star(ast_manager & m, params_ref const & p = params_ref()): + rewriter_tpl(m, false, m_cfg), + m_cfg(m, p) {} +}; + +#endif diff --git a/src/ast/rewriter/pb_rewriter.cpp b/src/ast/rewriter/pb_rewriter.cpp index cb42052b9..2e2c28edc 100644 --- a/src/ast/rewriter/pb_rewriter.cpp +++ b/src/ast/rewriter/pb_rewriter.cpp @@ -323,7 +323,7 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons break; } } - TRACE("pb", + TRACE("pb_verbose", expr_ref tmp(m); tmp = m.mk_app(f, num_args, args); tout << tmp << "\n"; diff --git a/src/ast/rewriter/rewriter.cpp b/src/ast/rewriter/rewriter.cpp index 80b690428..7e646871c 100644 --- a/src/ast/rewriter/rewriter.cpp +++ b/src/ast/rewriter/rewriter.cpp @@ -42,7 +42,7 @@ void rewriter_core::del_cache_stack() { } } -void rewriter_core::cache_result(expr * k, expr * v) { +void rewriter_core::cache_shifted_result(expr * k, unsigned offset, expr * v) { #if 0 // trace for tracking cache usage verbose_stream() << "1 " << k->get_id() << std::endl; @@ -53,7 +53,7 @@ void rewriter_core::cache_result(expr * k, expr * v) { SASSERT(m().get_sort(k) == m().get_sort(v)); - m_cache->insert(k, v); + m_cache->insert(k, offset, v); #if 0 static unsigned num_cached = 0; num_cached ++; diff --git a/src/ast/rewriter/rewriter.h b/src/ast/rewriter/rewriter.h index 4195e7de6..d380fa707 100644 --- a/src/ast/rewriter/rewriter.h +++ b/src/ast/rewriter/rewriter.h @@ -90,8 +90,10 @@ protected: void init_cache_stack(); void del_cache_stack(); void reset_cache(); - void cache_result(expr * k, expr * v); + void cache_result(expr * k, expr * v) { cache_shifted_result(k, 0, v); } + void cache_shifted_result(expr * k, unsigned offset, expr * v); expr * get_cached(expr * k) const { return m_cache->find(k); } + expr * get_cached(expr* k, unsigned offset) const { return m_cache->find(k, offset); } void cache_result(expr * k, expr * v, proof * pr); proof * get_cached_pr(expr * k) const { return static_cast(m_cache_pr->find(k)); } diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index 3ee1e4caf..0eeb1f52f 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -38,9 +38,10 @@ void rewriter_tpl::process_var(var * v) { if (!ProofGen) { // bindings are only used when Proof Generation is not enabled. unsigned idx = v->get_idx(); + if (idx < m_bindings.size()) { unsigned index = m_bindings.size() - idx - 1; - var * r = (var*)(m_bindings[index]); + expr * r = m_bindings[index]; if (r != nullptr) { CTRACE("rewriter", v->get_sort() != m().get_sort(r), tout << expr_ref(v, m()) << ":" << sort_ref(v->get_sort(), m()) << " != " << expr_ref(r, m()) << ":" << sort_ref(m().get_sort(r), m()); @@ -50,11 +51,18 @@ void rewriter_tpl::process_var(var * v) { if (!is_ground(r) && m_shifts[index] != m_bindings.size()) { unsigned shift_amount = m_bindings.size() - m_shifts[index]; + expr* c = get_cached(r, shift_amount); + if (c) { + result_stack().push_back(c); + set_new_child_flag(v); + return; + } expr_ref tmp(m()); m_shifter(r, shift_amount, tmp); result_stack().push_back(tmp); TRACE("rewriter", tout << "shift: " << shift_amount << " idx: " << idx << " --> " << tmp << "\n"; display_bindings(tout);); + cache_shifted_result(r, shift_amount, tmp); } else { result_stack().push_back(r); @@ -380,7 +388,6 @@ void rewriter_tpl::process_app(app * t, frame & fr) { TRACE("get_macro", display_bindings(tout);); begin_scope(); m_num_qvars += num_args; - //m_num_qvars = 0; m_root = def; push_frame(def, false, RW_UNBOUNDED_DEPTH); return; @@ -480,7 +487,7 @@ void rewriter_tpl::process_quantifier(quantifier * q, frame & fr) { m_root = q->get_expr(); unsigned sz = m_bindings.size(); for (unsigned i = 0; i < num_decls; i++) { - m_bindings.push_back(0); + m_bindings.push_back(nullptr); m_shifts.push_back(sz); } } @@ -514,7 +521,12 @@ void rewriter_tpl::process_quantifier(quantifier * q, frame & fr) { } if (ProofGen) { quantifier_ref new_q(m().update_quantifier(q, num_pats, new_pats.c_ptr(), num_no_pats, new_no_pats.c_ptr(), new_body), m()); - m_pr = q == new_q ? nullptr : m().mk_quant_intro(q, new_q, result_pr_stack().get(fr.m_spos)); + m_pr = nullptr; + if (q != new_q) { + m_pr = result_pr_stack().get(fr.m_spos); + m_pr = m().mk_bind_proof(q, m_pr); + m_pr = m().mk_quant_intro(q, new_q, m_pr); + } m_r = new_q; proof_ref pr2(m()); if (m_cfg.reduce_quantifier(new_q, new_body, new_pats.c_ptr(), new_no_pats.c_ptr(), m_r, pr2)) { diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index b67163f7c..cb915d86b 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -33,30 +33,32 @@ Notes: expr_ref sym_expr::accept(expr* e) { ast_manager& m = m_t.get_manager(); expr_ref result(m); + var_subst subst(m); + seq_util u(m); + unsigned r1, r2, r3; switch (m_ty) { - case t_pred: { - var_subst subst(m); + case t_pred: result = subst(m_t, 1, &e); + break; + case t_not: + result = m_expr->accept(e); + result = m.mk_not(result); break; - } case t_char: SASSERT(m.get_sort(e) == m.get_sort(m_t)); SASSERT(m.get_sort(e) == m_sort); result = m.mk_eq(e, m_t); break; - case t_range: { - bv_util bv(m); - rational r1, r2, r3; - unsigned sz; - if (bv.is_numeral(m_t, r1, sz) && bv.is_numeral(e, r2, sz) && bv.is_numeral(m_s, r3, sz)) { + case t_range: + if (u.is_const_char(m_t, r1) && u.is_const_char(e, r2) && u.is_const_char(m_s, r3)) { result = m.mk_bool_val((r1 <= r2) && (r2 <= r3)); } else { - result = m.mk_and(bv.mk_ule(m_t, e), bv.mk_ule(e, m_s)); + result = m.mk_and(u.mk_le(m_t, e), u.mk_le(e, m_s)); } break; } - } + return result; } @@ -65,6 +67,7 @@ std::ostream& sym_expr::display(std::ostream& out) const { case t_char: return out << m_t; case t_range: return out << m_t << ":" << m_s; case t_pred: return out << m_t; + case t_not: return m_expr->display(out << "not "); } return out << "expression type not recognized"; } @@ -80,10 +83,11 @@ struct display_expr1 { class sym_expr_boolean_algebra : public boolean_algebra { ast_manager& m; expr_solver& m_solver; + expr_ref m_var; typedef sym_expr* T; public: sym_expr_boolean_algebra(ast_manager& m, expr_solver& s): - m(m), m_solver(s) {} + m(m), m_solver(s), m_var(m) {} T mk_false() override { expr_ref fml(m.mk_false(), m); @@ -94,6 +98,7 @@ public: return sym_expr::mk_pred(fml, m.mk_bool_sort()); } T mk_and(T x, T y) override { + seq_util u(m); if (x->is_char() && y->is_char()) { if (x->get_char() == y->get_char()) { return x; @@ -103,6 +108,21 @@ public: return sym_expr::mk_pred(fml, x->get_sort()); } } + unsigned lo1, hi1, lo2, hi2; + if (x->is_range() && y->is_range() && + u.is_const_char(x->get_lo(), lo1) && u.is_const_char(x->get_hi(), hi1) && + u.is_const_char(y->get_lo(), lo2) && u.is_const_char(y->get_hi(), hi2)) { + lo1 = std::max(lo1, lo2); + hi1 = std::min(hi1, hi2); + if (lo1 > hi1) { + expr_ref fml(m.mk_false(), m); + return sym_expr::mk_pred(fml, x->get_sort()); + } + expr_ref _start(u.mk_char(lo1), m); + expr_ref _stop(u.mk_char(hi1), m); + return sym_expr::mk_range(_start, _stop); + } + sort* s = x->get_sort(); if (m.is_bool(s)) s = y->get_sort(); var_ref v(m.mk_var(0, s), m); @@ -111,13 +131,29 @@ public: if (m.is_true(fml1)) { return y; } - if (m.is_true(fml2)) return x; - if (fml1 == fml2) return x; + if (m.is_true(fml2)) { + return x; + } + if (fml1 == fml2) { + return x; + } + if (is_complement(fml1, fml2)) { + expr_ref ff(m.mk_false(), m); + return sym_expr::mk_pred(ff, x->get_sort()); + } bool_rewriter br(m); expr_ref fml(m); br.mk_and(fml1, fml2, fml); return sym_expr::mk_pred(fml, x->get_sort()); } + + bool is_complement(expr* f1, expr* f2) { + expr* f = nullptr; + return + (m.is_not(f1, f) && f == f2) || + (m.is_not(f2, f) && f == f1); + } + T mk_or(T x, T y) override { if (x->is_char() && y->is_char() && x->get_char() == y->get_char()) { @@ -148,6 +184,7 @@ public: } } } + T mk_or(unsigned sz, T const* ts) override { switch (sz) { case 0: return mk_false(); @@ -161,15 +198,24 @@ public: } } } + lbool is_sat(T x) override { + unsigned lo, hi; + seq_util u(m); + if (x->is_char()) { return l_true; } - if (x->is_range()) { - // TBD check lower is below upper. + if (x->is_range() && u.is_const_char(x->get_lo(), lo) && u.is_const_char(x->get_hi(), hi)) { + return (lo <= hi) ? l_true : l_false; } - expr_ref v(m.mk_fresh_const("x", x->get_sort()), m); - expr_ref fml = x->accept(v); + if (x->is_not() && x->get_arg()->is_range() && u.is_const_char(x->get_arg()->get_lo(), lo) && 0 < lo) { + return l_true; + } + if (!m_var || m.get_sort(m_var) != x->get_sort()) { + m_var = m.mk_fresh_const("x", x->get_sort()); + } + expr_ref fml = x->accept(m_var); if (m.is_true(fml)) { return l_true; } @@ -178,19 +224,14 @@ public: } return m_solver.check_sat(fml); } + T mk_not(T x) override { - var_ref v(m.mk_var(0, x->get_sort()), m); - expr_ref fml(m.mk_not(x->accept(v)), m); - return sym_expr::mk_pred(fml, x->get_sort()); + return sym_expr::mk_not(m, x); } - /*virtual vector, T>> generate_min_terms(vector constraints){ - - return 0; - }*/ }; -re2automaton::re2automaton(ast_manager& m): m(m), u(m), bv(m), m_ba(nullptr), m_sa(nullptr) {} +re2automaton::re2automaton(ast_manager& m): m(m), u(m), m_ba(nullptr), m_sa(nullptr) {} re2automaton::~re2automaton() {} @@ -248,9 +289,8 @@ eautomaton* re2automaton::re2aut(expr* e) { s1.length() == 1 && s2.length() == 1) { unsigned start = s1[0]; unsigned stop = s2[0]; - unsigned nb = s1.num_bits(); - expr_ref _start(bv.mk_numeral(start, nb), m); - expr_ref _stop(bv.mk_numeral(stop, nb), m); + expr_ref _start(u.mk_char(start), m); + expr_ref _stop(u.mk_char(stop), m); TRACE("seq", tout << "Range: " << start << " " << stop << "\n";); a = alloc(eautomaton, sm, sym_expr::mk_range(_start, _stop)); return a.detach(); @@ -309,6 +349,9 @@ eautomaton* re2automaton::re2aut(expr* e) { else if (u.re.is_intersection(e, e1, e2) && m_sa && (a = re2aut(e1)) && (b = re2aut(e2))) { return m_sa->mk_product(*a, *b); } + else { + TRACE("seq", tout << "not handled " << mk_pp(e, m) << "\n";); + } return nullptr; } @@ -343,9 +386,9 @@ eautomaton* re2automaton::seq2aut(expr* e) { br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_family_id() == get_fid()); - + br_status st = BR_FAILED; switch(f->get_decl_kind()) { - + case OP_SEQ_UNIT: SASSERT(num_args == 1); return mk_seq_unit(args[0], result); @@ -356,16 +399,19 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con return mk_re_plus(args[0], result); case OP_RE_STAR: SASSERT(num_args == 1); - return mk_re_star(args[0], result); + st = mk_re_star(args[0], result); + break; case OP_RE_OPTION: SASSERT(num_args == 1); return mk_re_opt(args[0], result); case OP_RE_CONCAT: if (num_args == 1) { - result = args[0]; return BR_DONE; + result = args[0]; + return BR_DONE; } SASSERT(num_args == 2); - return mk_re_concat(args[0], args[1], result); + st = mk_re_concat(args[0], args[1], result); + break; case OP_RE_UNION: if (num_args == 1) { result = args[0]; return BR_DONE; @@ -405,13 +451,19 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con return mk_seq_length(args[0], result); case OP_SEQ_EXTRACT: SASSERT(num_args == 3); - return mk_seq_extract(args[0], args[1], args[2], result); + st = mk_seq_extract(args[0], args[1], args[2], result); + break; case OP_SEQ_CONTAINS: SASSERT(num_args == 2); return mk_seq_contains(args[0], args[1], result); case OP_SEQ_AT: SASSERT(num_args == 2); return mk_seq_at(args[0], args[1], result); +#if 0 + case OP_SEQ_NTH: + SASSERT(num_args == 2); + return mk_seq_nth(args[0], args[1], result); +#endif case OP_SEQ_PREFIX: SASSERT(num_args == 2); return mk_seq_prefix(args[0], args[1], result); @@ -456,21 +508,20 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con case _OP_STRING_STRIDOF: UNREACHABLE(); } - return BR_FAILED; + CTRACE("seq", st != BR_FAILED, tout << result << "\n";); + return st; } /* * (seq.unit (_ BitVector 8)) ==> String constant */ br_status seq_rewriter::mk_seq_unit(expr* e, expr_ref& result) { - bv_util bvu(m()); - rational n_val; - unsigned int n_size; + unsigned ch; // specifically we want (_ BitVector 8) - if (bvu.is_bv(e) && bvu.is_numeral(e, n_val, n_size) && n_size == 8) { + if (m_util.is_const_char(e, ch)) { // convert to string constant - zstring str(n_val.get_unsigned()); - TRACE("seq_verbose", tout << "rewrite seq.unit of 8-bit value " << n_val.to_string() << " to string constant \"" << str<< "\"" << std::endl;); + zstring str(ch); + TRACE("seq_verbose", tout << "rewrite seq.unit of 8-bit value " << ch << " to string constant \"" << str<< "\"" << std::endl;); result = m_util.str.mk_string(str); return BR_DONE; } @@ -566,6 +617,7 @@ br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& resu bool constantPos = m_autil.is_numeral(b, pos); bool constantLen = m_autil.is_numeral(c, len); + // case 1: pos<0 or len<=0 // rewrite to "" if ( (constantPos && pos.is_neg()) || (constantLen && !len.is_pos()) ) { @@ -574,7 +626,7 @@ br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& resu } // case 1.1: pos >= length(base) // rewrite to "" - if (constantBase && constantPos && pos >= rational(s.length())) { + if (constantPos && constantBase && pos >= rational(s.length())) { result = m_util.str.mk_empty(m().get_sort(a)); return BR_DONE; } @@ -582,52 +634,73 @@ br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& resu constantPos &= pos.is_unsigned(); constantLen &= len.is_unsigned(); - if (constantBase && constantPos && constantLen) { - if (pos.get_unsigned() + len.get_unsigned() >= s.length()) { - // case 2: pos+len goes past the end of the string - unsigned _len = s.length() - pos.get_unsigned() + 1; - result = m_util.str.mk_string(s.extract(pos.get_unsigned(), _len)); - } else { - // case 3: pos+len still within string - result = m_util.str.mk_string(s.extract(pos.get_unsigned(), len.get_unsigned())); - } - return BR_DONE; - } - - if (constantPos && constantLen) { + if (constantPos && constantLen && constantBase) { unsigned _pos = pos.get_unsigned(); unsigned _len = len.get_unsigned(); - SASSERT(_len > 0); - expr_ref_vector as(m()), bs(m()); - m_util.str.get_concat(a, as); - if (as.empty()) { - result = a; - return BR_DONE; - } - for (unsigned i = 0; i < as.size() && _len > 0; ++i) { - if (m_util.str.is_unit(as[i].get())) { - if (_pos == 0) { - bs.push_back(as[i].get()); - --_len; - } - else { - --_pos; - } - } - else { - return BR_FAILED; - } - } - if (bs.empty()) { - result = m_util.str.mk_empty(m().get_sort(a)); - } - else { - result = m_util.str.mk_concat(bs); + if (_pos + _len >= s.length()) { + // case 2: pos+len goes past the end of the string + unsigned _len = s.length() - _pos + 1; + result = m_util.str.mk_string(s.extract(_pos, _len)); + } else { + // case 3: pos+len still within string + result = m_util.str.mk_string(s.extract(_pos, _len)); } return BR_DONE; } - return BR_FAILED; + + expr_ref_vector as(m()), bs(m()); + m_util.str.get_concat_units(a, as); + if (as.empty()) { + result = m_util.str.mk_empty(m().get_sort(a)); + return BR_DONE; + } + + if (!constantPos) { + return BR_FAILED; + } + unsigned _pos = pos.get_unsigned(); + + // (extract s 0 (len s)) = s + expr* a2 = nullptr; + if (_pos == 0 && m_util.str.is_length(c, a2) && a == a2) { + result = a; + return BR_DONE; + } + + unsigned offset = 0; + for (; offset < as.size() && m_util.str.is_unit(as.get(offset)) && offset < _pos; ++offset) {}; + if (offset == 0 && _pos > 0) { + return BR_FAILED; + } + if (_pos == 0 && !constantLen) { + return BR_FAILED; + } + // (extract (++ (unit x) (unit y)) 3 c) = empty + if (offset == as.size()) { + result = m_util.str.mk_empty(m().get_sort(a)); + return BR_DONE; + } + SASSERT(offset != 0 || _pos == 0); + + if (constantLen && _pos == offset) { + unsigned _len = len.get_unsigned(); + // (extract (++ (unit a) (unit b) (unit c) x) 1 2) = (++ (unit b) (unit c)) + unsigned i = offset; + for (; i < as.size() && m_util.str.is_unit(as.get(i)) && i - offset < _len; ++i); + if (i - offset == _len) { + result = m_util.str.mk_concat(_len, as.c_ptr() + offset); + return BR_DONE; + } + } + if (offset == 0) { + return BR_FAILED; + } + expr_ref pos1(m()); + pos1 = m_autil.mk_sub(b, m_autil.mk_int(offset)); + result = m_util.str.mk_concat(as.size() - offset, as.c_ptr() + offset); + result = m_util.str.mk_substr(result, pos1, c); + return BR_REWRITE3; } bool seq_rewriter::cannot_contain_suffix(expr* a, expr* b) { @@ -809,6 +882,32 @@ br_status seq_rewriter::mk_seq_at(expr* a, expr* b, expr_ref& result) { return BR_DONE; } +br_status seq_rewriter::mk_seq_nth(expr* a, expr* b, expr_ref& result) { + zstring c; + rational r; + if (!m_autil.is_numeral(b, r) || !r.is_unsigned()) { + return BR_FAILED; + } + unsigned len = r.get_unsigned(); + + expr_ref_vector as(m()); + m_util.str.get_concat_units(a, as); + + for (unsigned i = 0; i < as.size(); ++i) { + expr* a = as.get(i), *u = nullptr; + if (m_util.str.is_unit(a, u)) { + if (len == i) { + result = u; + return BR_DONE; + } + } + else { + return BR_FAILED; + } + } + return BR_FAILED; +} + br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result) { zstring s1, s2; rational r; @@ -1238,6 +1337,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { scoped_ptr aut; expr_ref_vector seq(m()); if (!(aut = m_re2aut(b))) { + TRACE("seq", tout << "not translated to automaton " << mk_pp(b, m()) << "\n";); return BR_FAILED; } @@ -1254,6 +1354,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { } if (!is_sequence(a, seq)) { + TRACE("seq", tout << "not a sequence " << mk_pp(a, m()) << "\n";); return BR_FAILED; } @@ -1305,17 +1406,16 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { } } u_map const& frontier = maps[select_map]; - u_map::iterator it = frontier.begin(), end = frontier.end(); expr_ref_vector ors(m()); - for (; it != end; ++it) { + for (auto const& kv : frontier) { unsigned_vector states; bool has_final = false; - aut->get_epsilon_closure(it->m_key, states); + aut->get_epsilon_closure(kv.m_key, states); for (unsigned i = 0; i < states.size() && !has_final; ++i) { has_final = aut->is_final_state(states[i]); } if (has_final) { - ors.push_back(it->m_value); + ors.push_back(kv.m_value); } } result = mk_or(ors); @@ -1434,6 +1534,14 @@ br_status seq_rewriter::mk_re_inter(expr* a, expr* b, expr_ref& result) { result = a; return BR_DONE; } + expr* ac = nullptr, *bc = nullptr; + if ((m_util.re.is_complement(a, ac) && ac == b) || + (m_util.re.is_complement(b, bc) && bc == a)) { + sort* seq_sort = nullptr; + VERIFY(m_util.is_re(a, seq_sort)); + result = m_util.re.mk_empty(seq_sort); + return BR_DONE; + } return BR_FAILED; } @@ -1977,15 +2085,13 @@ bool seq_rewriter::min_length(unsigned n, expr* const* es, unsigned& len) { bool seq_rewriter::is_string(unsigned n, expr* const* es, zstring& s) const { zstring s1; expr* e; - bv_util bv(m()); - rational val; - unsigned sz; + unsigned ch; for (unsigned i = 0; i < n; ++i) { if (m_util.str.is_string(es[i], s1)) { s = s + s1; } - else if (m_util.str.is_unit(es[i], e) && bv.is_numeral(e, val, sz)) { - s = s + zstring(val.get_unsigned()); + else if (m_util.str.is_unit(es[i], e) && m_util.is_const_char(e, ch)) { + s = s + zstring(ch); } else { return false; diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index f5878b2c2..25b8979fc 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -31,31 +31,38 @@ class sym_expr { enum ty { t_char, t_pred, + t_not, t_range }; - ty m_ty; - sort* m_sort; - expr_ref m_t; - expr_ref m_s; - unsigned m_ref; - sym_expr(ty ty, expr_ref& t, expr_ref& s, sort* srt) : m_ty(ty), m_sort(srt), m_t(t), m_s(s), m_ref(0) {} + ty m_ty; + sort* m_sort; + sym_expr* m_expr; + expr_ref m_t; + expr_ref m_s; + unsigned m_ref; + sym_expr(ty ty, expr_ref& t, expr_ref& s, sort* srt, sym_expr* e) : + m_ty(ty), m_sort(srt), m_expr(e), m_t(t), m_s(s), m_ref(0) {} public: + ~sym_expr() { if (m_expr) m_expr->dec_ref(); } expr_ref accept(expr* e); - static sym_expr* mk_char(expr_ref& t) { return alloc(sym_expr, t_char, t, t, t.get_manager().get_sort(t)); } + static sym_expr* mk_char(expr_ref& t) { return alloc(sym_expr, t_char, t, t, t.get_manager().get_sort(t), nullptr); } static sym_expr* mk_char(ast_manager& m, expr* t) { expr_ref tr(t, m); return mk_char(tr); } - static sym_expr* mk_pred(expr_ref& t, sort* s) { return alloc(sym_expr, t_pred, t, t, s); } - static sym_expr* mk_range(expr_ref& lo, expr_ref& hi) { return alloc(sym_expr, t_range, lo, hi, lo.get_manager().get_sort(hi)); } + static sym_expr* mk_pred(expr_ref& t, sort* s) { return alloc(sym_expr, t_pred, t, t, s, nullptr); } + static sym_expr* mk_range(expr_ref& lo, expr_ref& hi) { return alloc(sym_expr, t_range, lo, hi, lo.get_manager().get_sort(hi), nullptr); } + static sym_expr* mk_not(ast_manager& m, sym_expr* e) { expr_ref f(m); e->inc_ref(); return alloc(sym_expr, t_not, f, f, e->get_sort(), e); } void inc_ref() { ++m_ref; } void dec_ref() { --m_ref; if (m_ref == 0) dealloc(this); } std::ostream& display(std::ostream& out) const; bool is_char() const { return m_ty == t_char; } bool is_pred() const { return !is_char(); } bool is_range() const { return m_ty == t_range; } + bool is_not() const { return m_ty == t_not; } sort* get_sort() const { return m_sort; } expr* get_char() const { SASSERT(is_char()); return m_t; } expr* get_pred() const { SASSERT(is_pred()); return m_t; } expr* get_lo() const { SASSERT(is_range()); return m_t; } expr* get_hi() const { SASSERT(is_range()); return m_s; } + sym_expr* get_arg() const { SASSERT(is_not()); return m_expr; } }; class sym_expr_manager { @@ -77,7 +84,6 @@ class re2automaton { ast_manager& m; sym_expr_manager sm; seq_util u; - bv_util bv; scoped_ptr m_solver; scoped_ptr m_ba; scoped_ptr m_sa; @@ -108,6 +114,7 @@ class seq_rewriter { br_status mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& result); br_status mk_seq_contains(expr* a, expr* b, expr_ref& result); br_status mk_seq_at(expr* a, expr* b, expr_ref& result); + br_status mk_seq_nth(expr* a, expr* b, expr_ref& result); br_status mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result); br_status mk_seq_replace(expr* a, expr* b, expr* c, expr_ref& result); br_status mk_seq_prefix(expr* a, expr* b, expr_ref& result); diff --git a/src/ast/rewriter/var_subst.cpp b/src/ast/rewriter/var_subst.cpp index cbd79e08c..ff479b565 100644 --- a/src/ast/rewriter/var_subst.cpp +++ b/src/ast/rewriter/var_subst.cpp @@ -135,17 +135,14 @@ expr_ref unused_vars_eliminator::operator()(quantifier* q) { return result; } - expr_ref tmp(m); expr_ref_buffer new_patterns(m); expr_ref_buffer new_no_patterns(m); for (unsigned i = 0; i < num_patterns; i++) { - tmp = m_subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr()); - new_patterns.push_back(tmp); + new_patterns.push_back(m_subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr())); } for (unsigned i = 0; i < num_no_patterns; i++) { - tmp = m_subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr()); - new_no_patterns.push_back(tmp); + new_no_patterns.push_back(m_subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr())); } result = m.mk_quantifier(q->get_kind(), diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 8fc130ef1..20e1fb36c 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -20,6 +20,7 @@ Revision History: #include "ast/arith_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/ast_pp.h" +#include "ast/bv_decl_plugin.h" #include static bool is_hex_digit(char ch, unsigned& d) { @@ -68,14 +69,14 @@ static bool is_escape_char(char const *& s, unsigned& result) { } /* 2 octal digits */ if (is_octal_digit(*(s + 1), d1) && is_octal_digit(*(s + 2), d2) && - !is_octal_digit(*(s + 3), d3)) { + !is_octal_digit(*(s + 3), d3)) { result = d1 * 8 + d2; s += 3; return true; } /* 3 octal digits */ if (is_octal_digit(*(s + 1), d1) && is_octal_digit(*(s + 2), d2) && - is_octal_digit(*(s + 3), d3)) { + is_octal_digit(*(s + 3), d3)) { result = d1*64 + d2*8 + d3; s += 4; return true; @@ -295,13 +296,10 @@ bool zstring::operator==(const zstring& other) const { return false; } for (unsigned i = 0; i < length(); ++i) { - unsigned Xi = m_buffer[i]; - unsigned Yi = other[i]; - if (Xi != Yi) { + if (m_buffer[i] != other[i]) { return false; } } - return true; } @@ -324,19 +322,14 @@ bool operator<(const zstring& lhs, const zstring& rhs) { unsigned Ri = rhs[i]; if (Li < Ri) { return true; - } else if (Li > Ri) { + } + else if (Li > Ri) { return false; - } else { - continue; - } + } } // at this point, all compared characters are equal, // so decide based on the relative lengths - if (lhs.length() < rhs.length()) { - return true; - } else { - return false; - } + return lhs.length() < rhs.length(); } @@ -377,8 +370,8 @@ bool seq_decl_plugin::match(ptr_vector& binding, sort* s, sort* sP) { if (s->get_family_id() == sP->get_family_id() && s->get_decl_kind() == sP->get_decl_kind() && s->get_num_parameters() == sP->get_num_parameters()) { - for (unsigned i = 0, sz = s->get_num_parameters(); i < sz; ++i) { - parameter const& p = s->get_parameter(i); + for (unsigned i = 0, sz = s->get_num_parameters(); i < sz; ++i) { + parameter const& p = s->get_parameter(i); if (p.is_ast() && is_sort(p.get_ast())) { parameter const& p2 = sP->get_parameter(i); if (!match(binding, to_sort(p.get_ast()), to_sort(p2.get_ast()))) return false; @@ -435,7 +428,7 @@ void seq_decl_plugin::match_right_assoc(psig& sig, unsigned dsz, sort *const* do } void seq_decl_plugin::match(psig& sig, unsigned dsz, sort *const* dom, sort* range, sort_ref& range_out) { - ptr_vector binding; + m_binding.reset(); ast_manager& m = *m_manager; if (sig.m_dom.size() != dsz) { std::ostringstream strm; @@ -445,10 +438,10 @@ void seq_decl_plugin::match(psig& sig, unsigned dsz, sort *const* dom, sort* ran } bool is_match = true; for (unsigned i = 0; is_match && i < dsz; ++i) { - is_match = match(binding, dom[i], sig.m_dom[i].get()); + is_match = match(m_binding, dom[i], sig.m_dom[i].get()); } if (range && is_match) { - is_match = match(binding, range, sig.m_range); + is_match = match(m_binding, range, sig.m_range); } if (!is_match) { std::ostringstream strm; @@ -474,7 +467,7 @@ void seq_decl_plugin::match(psig& sig, unsigned dsz, sort *const* dom, sort* ran strm << "is ambiguous. Function takes no arguments and sort of range has not been constrained"; m.raise_exception(strm.str().c_str()); } - range_out = apply_binding(binding, sig.m_range); + range_out = apply_binding(m_binding, sig.m_range); SASSERT(range_out); } @@ -555,7 +548,7 @@ void seq_decl_plugin::init() { m_sigs[OP_RE_OF_PRED] = alloc(psig, m, "re.of.pred", 1, 1, &predA, reA); m_sigs[OP_SEQ_TO_RE] = alloc(psig, m, "seq.to.re", 1, 1, &seqA, reA); m_sigs[OP_SEQ_IN_RE] = alloc(psig, m, "seq.in.re", 1, 2, seqAreA, boolT); - m_sigs[OP_STRING_CONST] = 0; + m_sigs[OP_STRING_CONST] = nullptr; m_sigs[_OP_STRING_STRIDOF] = alloc(psig, m, "str.indexof", 0, 3, str2TintT, intT); m_sigs[_OP_STRING_STRREPL] = alloc(psig, m, "str.replace", 0, 3, str3T, strT); m_sigs[OP_STRING_ITOS] = alloc(psig, m, "int.to.str", 0, 1, &intT, strT); @@ -967,6 +960,24 @@ app* seq_util::str::mk_char(char ch) const { return mk_char(s, 0); } +bool seq_util::is_const_char(expr* e, unsigned& c) const { + bv_util bv(m); + rational r; + unsigned sz; + return bv.is_numeral(e, r, sz) && sz == 8 && r.is_unsigned() && (c = r.get_unsigned(), true); +} + +app* seq_util::mk_char(unsigned ch) const { + bv_util bv(m); + return bv.mk_numeral(rational(ch), 8); +} + +app* seq_util::mk_le(expr* ch1, expr* ch2) const { + bv_util bv(m); + return bv.mk_ule(ch1, ch2); +} + + bool seq_util::str::is_string(expr const* n, zstring& s) const { if (is_string(n)) { s = zstring(to_app(n)->get_decl()->get_parameter(0).get_symbol().bare_str()); @@ -1044,7 +1055,6 @@ app* seq_util::re::mk_empty(sort* s) { return m.mk_app(m_fid, OP_RE_EMPTY_SET, 0, nullptr, 0, nullptr, s); } - bool seq_util::re::is_loop(expr const* n, expr*& body, unsigned& lo, unsigned& hi) { if (is_loop(n)) { app const* a = to_app(n); diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index f8107f1e0..1148c8411 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -22,7 +22,6 @@ Revision History: #define SEQ_DECL_PLUGIN_H_ #include "ast/ast.h" -#include "ast/bv_decl_plugin.h" enum seq_sort_kind { @@ -140,6 +139,7 @@ class seq_decl_plugin : public decl_plugin { }; ptr_vector m_sigs; + ptr_vector m_binding; bool m_init; symbol m_stringc_sym; symbol m_charc_sym; @@ -220,6 +220,9 @@ public: bool is_re(expr* e) const { return is_re(m.get_sort(e)); } bool is_re(expr* e, sort*& seq) const { return is_re(m.get_sort(e), seq); } bool is_char(expr* e) const { return is_char(m.get_sort(e)); } + bool is_const_char(expr* e, unsigned& c) const; + app* mk_char(unsigned ch) const; + app* mk_le(expr* ch1, expr* ch2) const; app* mk_skolem(symbol const& name, unsigned n, expr* const* args, sort* range); bool is_skolem(expr const* e) const { return is_app_of(e, m_fid, _OP_SEQ_SKOLEM); } diff --git a/src/ast/shared_occs.cpp b/src/ast/shared_occs.cpp index 89867e8c1..9528892e7 100644 --- a/src/ast/shared_occs.cpp +++ b/src/ast/shared_occs.cpp @@ -21,13 +21,11 @@ Revision History: #include "util/ref_util.h" inline void shared_occs::insert(expr * t) { - obj_hashtable::entry * dummy; - if (m_shared.insert_if_not_there_core(t, dummy)) - m.inc_ref(t); + m_shared.reserve(t->get_id() + 1); + m_shared[t->get_id()] = t; } void shared_occs::reset() { - dec_ref_collection_values(m, m_shared); m_shared.reset(); } @@ -132,9 +130,15 @@ void shared_occs::operator()(expr * t) { } void shared_occs::display(std::ostream & out, ast_manager & m) const { - iterator it = begin_shared(); - iterator end = end_shared(); - for (; it != end; ++it) { - out << mk_ismt2_pp(*it, m) << "\n"; + for (expr* s : m_shared) { + if (s) { + out << mk_ismt2_pp(s, m) << "\n"; + } } } + +unsigned shared_occs::num_shared() const{ + unsigned count = 0; + for (expr* s : m_shared) if (s) count++; + return count; +} diff --git a/src/ast/shared_occs.h b/src/ast/shared_occs.h index 40921922a..300bb584f 100644 --- a/src/ast/shared_occs.h +++ b/src/ast/shared_occs.h @@ -53,7 +53,7 @@ class shared_occs { bool m_track_atomic; bool m_visit_quantifiers; bool m_visit_patterns; - obj_hashtable m_shared; + expr_ref_vector m_shared; typedef std::pair frame; svector m_stack; bool process(expr * t, shared_occs_mark & visited); @@ -64,15 +64,14 @@ public: m(_m), m_track_atomic(track_atomic), m_visit_quantifiers(visit_quantifiers), - m_visit_patterns(visit_patterns) { + m_visit_patterns(visit_patterns), + m_shared(m) { } ~shared_occs(); void operator()(expr * t); void operator()(expr * t, shared_occs_mark & visited); - bool is_shared(expr * t) const { return m_shared.contains(t); } - unsigned num_shared() const { return m_shared.size(); } - iterator begin_shared() const { return m_shared.begin(); } - iterator end_shared() const { return m_shared.end(); } + bool is_shared(expr * t) const { return m_shared.get(t->get_id(), nullptr) != nullptr; } + unsigned num_shared() const; void reset(); void cleanup(); void display(std::ostream & out, ast_manager & mgr) const; diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index ea5994ece..80cd6373e 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -137,7 +137,7 @@ ATOMIC_CMD(get_assignment_cmd, "get-assignment", "retrieve assignment", { symbol const & name = kv.m_key; macro_decls const & _m = kv.m_value; for (auto md : _m) { - if (md.m_domain.size() == 0 && ctx.m().is_bool(md.m_body)) { + if (md.m_domain.empty() && ctx.m().is_bool(md.m_body)) { model::scoped_model_completion _scm(*m, true); expr_ref val = (*m)(md.m_body); if (ctx.m().is_true(val) || ctx.m().is_false(val)) { @@ -309,7 +309,6 @@ protected: symbol m_produce_unsat_assumptions; symbol m_produce_models; symbol m_produce_assignments; - symbol m_produce_interpolants; symbol m_produce_assertions; symbol m_regular_output_channel; symbol m_diagnostic_output_channel; @@ -326,7 +325,7 @@ protected: return s == m_print_success || s == m_print_warning || s == m_expand_definitions || s == m_interactive_mode || s == m_produce_proofs || s == m_produce_unsat_cores || s == m_produce_unsat_assumptions || - s == m_produce_models || s == m_produce_assignments || s == m_produce_interpolants || + s == m_produce_models || s == m_produce_assignments || s == m_regular_output_channel || s == m_diagnostic_output_channel || s == m_random_seed || s == m_verbosity || s == m_global_decls || s == m_global_declarations || s == m_produce_assertions || s == m_reproducible_resource_limit; @@ -346,7 +345,6 @@ public: m_produce_unsat_assumptions(":produce-unsat-assumptions"), m_produce_models(":produce-models"), m_produce_assignments(":produce-assignments"), - m_produce_interpolants(":produce-interpolants"), m_produce_assertions(":produce-assertions"), m_regular_output_channel(":regular-output-channel"), m_diagnostic_output_channel(":diagnostic-output-channel"), @@ -418,10 +416,6 @@ class set_option_cmd : public set_get_option_cmd { check_not_initialized(ctx, m_produce_proofs); ctx.set_produce_proofs(to_bool(value)); } - else if (m_option == m_produce_interpolants) { - check_not_initialized(ctx, m_produce_interpolants); - ctx.set_produce_interpolants(to_bool(value)); - } else if (m_option == m_produce_unsat_cores) { check_not_initialized(ctx, m_produce_unsat_cores); ctx.set_produce_unsat_cores(to_bool(value)); @@ -577,9 +571,6 @@ public: else if (opt == m_produce_proofs) { print_bool(ctx, ctx.produce_proofs()); } - else if (opt == m_produce_interpolants) { - print_bool(ctx, ctx.produce_interpolants()); - } else if (opt == m_produce_unsat_cores) { print_bool(ctx, ctx.produce_unsat_cores()); } diff --git a/src/cmd_context/check_logic.cpp b/src/cmd_context/check_logic.cpp index 47c919d4a..35bbde2b2 100644 --- a/src/cmd_context/check_logic.cpp +++ b/src/cmd_context/check_logic.cpp @@ -237,10 +237,12 @@ struct check_logic::imp { return; } else if (m_bv_arrays) { - if (get_array_arity(s) != 1) - fail("logic supports only unidimensional arrays"); - if (!m_bv_util.is_bv_sort(get_array_range(s)) || !m_bv_util.is_bv_sort(get_array_domain(s, 0))) - fail("logic supports only arrays from bitvectors to bitvectors"); + unsigned sz = get_array_arity(s); + for (unsigned i = 0; i < sz; ++i) { + if (!m_bv_util.is_bv_sort(get_array_domain(s, i))) + fail("logic supports only arrays from bitvectors to bitvectors"); + } + check_sort(get_array_range(s)); } else { fail("logic does not support arrays"); diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index fb81c673e..c58453460 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -560,14 +560,6 @@ void cmd_context::set_produce_proofs(bool f) { m_params.m_proof = f; } -void cmd_context::set_produce_interpolants(bool f) { - // can only be set before initialization - // FIXME currently synonym for produce_proofs - // also sets the default solver to be simple smt - SASSERT(!has_manager()); - m_params.m_proof = f; - // set_solver_factory(mk_smt_solver_factory()); -} bool cmd_context::produce_models() const { return m_params.m_model; @@ -577,11 +569,6 @@ bool cmd_context::produce_proofs() const { return m_params.m_proof; } -bool cmd_context::produce_interpolants() const { - // FIXME currently synonym for produce_proofs - return m_params.m_proof; -} - bool cmd_context::produce_unsat_cores() const { return m_params.m_unsat_core; } @@ -1895,25 +1882,14 @@ void cmd_context::validate_model() { } } -// FIXME: really interpolants_enabled ought to be a parameter to the solver factory, -// but this is a global change, so for now, we use an alternate solver factory -// for interpolation void cmd_context::mk_solver() { bool proofs_enabled, models_enabled, unsat_core_enabled; params_ref p; m_params.get_solver_params(m(), p, proofs_enabled, models_enabled, unsat_core_enabled); - if (produce_interpolants() && m_interpolating_solver_factory) { - m_solver = (*m_interpolating_solver_factory)(m(), p, true /* must have proofs */, models_enabled, unsat_core_enabled, m_logic); - } - else - m_solver = (*m_solver_factory)(m(), p, proofs_enabled, models_enabled, unsat_core_enabled, m_logic); + m_solver = (*m_solver_factory)(m(), p, proofs_enabled, models_enabled, unsat_core_enabled, m_logic); } -void cmd_context::set_interpolating_solver_factory(solver_factory * f) { - SASSERT(!has_manager()); - m_interpolating_solver_factory = f; -} void cmd_context::set_solver_factory(solver_factory * f) { m_solver_factory = f; @@ -2108,7 +2084,7 @@ void cmd_context::dt_eh::operator()(sort * dt, pdecl* pd) { m_owner.insert(a); } } - if (m_owner.m_scopes.size() > 0) { + if (!m_owner.m_scopes.empty()) { m_owner.pm().inc_ref(pd); m_owner.m_psort_inst_stack.push_back(pd); } diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index b2ed1e5cb..55ea65cb5 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -33,6 +33,7 @@ Notes: #include "ast/ast_printer.h" #include "ast/datatype_decl_plugin.h" #include "ast/recfun_decl_plugin.h" +#include "ast/rewriter/seq_rewriter.h" #include "tactic/generic_model_converter.h" #include "solver/solver.h" #include "solver/progress_callback.h" @@ -240,7 +241,6 @@ protected: svector m_scopes; scoped_ptr m_solver_factory; - scoped_ptr m_interpolating_solver_factory; ref m_solver; ref m_check_sat_result; ref m_opt; @@ -315,7 +315,6 @@ public: void set_cancel(bool f); context_params & params() { return m_params; } solver_factory &get_solver_factory() { return *m_solver_factory; } - solver_factory &get_interpolating_solver_factory() { return *m_interpolating_solver_factory; } opt_wrapper* get_opt(); void set_opt(opt_wrapper* o); void global_params_updated(); // this method should be invoked when global (and module) params are updated. @@ -341,14 +340,12 @@ public: void set_random_seed(unsigned s) { m_random_seed = s; } bool produce_models() const; bool produce_proofs() const; - bool produce_interpolants() const; bool produce_unsat_cores() const; bool well_sorted_check_enabled() const; bool validate_model_enabled() const; void set_produce_models(bool flag); void set_produce_unsat_cores(bool flag); void set_produce_proofs(bool flag); - void set_produce_interpolants(bool flag); void set_produce_unsat_assumptions(bool flag) { m_produce_unsat_assumptions = flag; } bool produce_assignments() const { return m_produce_assignments; } bool produce_unsat_assumptions() const { return m_produce_unsat_assumptions; } @@ -364,7 +361,6 @@ public: sexpr_manager & sm() const { if (!m_sexpr_manager) const_cast(this)->m_sexpr_manager = alloc(sexpr_manager); return *m_sexpr_manager; } void set_solver_factory(solver_factory * s); - void set_interpolating_solver_factory(solver_factory * s); void set_check_sat_result(check_sat_result * r) { m_check_sat_result = r; } check_sat_result * get_check_sat_result() const { return m_check_sat_result.get(); } check_sat_state cs_state() const; @@ -493,4 +489,24 @@ public: std::ostream & operator<<(std::ostream & out, cmd_context::status st); + +class th_solver : public expr_solver { + cmd_context& m_ctx; + params_ref m_params; + ref m_solver; +public: + th_solver(cmd_context& ctx): m_ctx(ctx) {} + + lbool check_sat(expr* e) override { + if (!m_solver) { + m_solver = m_ctx.get_solver_factory()(m_ctx.m(), m_params, false, true, false, symbol::null); + } + m_solver->push(); + m_solver->assert_expr(e); + lbool r = m_solver->check_sat(0,nullptr); + m_solver->pop(1); + return r; + } +}; + #endif diff --git a/src/cmd_context/context_params.cpp b/src/cmd_context/context_params.cpp index c0ab1e3e2..447718aa9 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -166,8 +166,8 @@ void context_params::updt_params(params_ref const & p) { } void context_params::collect_param_descrs(param_descrs & d) { - d.insert("timeout", CPK_UINT, "default timeout (in milliseconds) used for solvers", "4294967295"); - d.insert("rlimit", CPK_UINT, "default resource limit used for solvers. Unrestricted when set to 0.", "0"); + insert_rlimit(d); + insert_timeout(d); d.insert("well_sorted_check", CPK_BOOL, "type checker", "false"); d.insert("type_check", CPK_BOOL, "type checker (alias for well_sorted_check)", "true"); d.insert("auto_config", CPK_BOOL, "use heuristics to automatically select solver and configure it", "true"); diff --git a/src/cmd_context/eval_cmd.cpp b/src/cmd_context/eval_cmd.cpp index 3f564edff..a599cfa4a 100644 --- a/src/cmd_context/eval_cmd.cpp +++ b/src/cmd_context/eval_cmd.cpp @@ -72,6 +72,7 @@ public: unsigned timeout = m_params.get_uint("timeout", UINT_MAX); unsigned rlimit = m_params.get_uint("rlimit", 0); model_evaluator ev(*(md.get()), m_params); + ev.set_solver(alloc(th_solver, ctx)); cancel_eh eh(ctx.m().limit()); { scoped_ctrl_c ctrlc(eh); diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index bdd2c8b97..7c1383d62 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -77,30 +77,12 @@ BINARY_SYM_CMD(shift_vars_cmd, store_expr_ref(ctx, m_sym, r.get()); }); -UNARY_CMD(pp_shared_cmd, "dbg-pp-shared", "", "display shared subterms of the given term", CPK_EXPR, expr *, { - shared_occs s(ctx.m()); - s(arg); - ctx.regular_stream() << "(shared"; - shared_occs::iterator it = s.begin_shared(); - shared_occs::iterator end = s.end_shared(); - for (; it != end; ++it) { - expr * curr = *it; - ctx.regular_stream() << std::endl << " "; - ctx.display(ctx.regular_stream(), curr, 2); - } - ctx.regular_stream() << ")" << std::endl; -}); UNARY_CMD(assert_not_cmd, "assert-not", "", "assert negation", CPK_EXPR, expr *, { expr_ref ne(ctx.m().mk_not(arg), ctx.m()); ctx.assert_expr(ne); }); -UNARY_CMD(num_shared_cmd, "dbg-num-shared", "", "return the number of shared subterms", CPK_EXPR, expr *, { - shared_occs s(ctx.m()); - s(arg); - ctx.regular_stream() << s.num_shared() << std::endl; -}); UNARY_CMD(size_cmd, "dbg-size", "", "return the size of the given term", CPK_EXPR, expr *, { ctx.regular_stream() << get_num_exprs(arg) << std::endl; @@ -524,11 +506,21 @@ public: for (func_decl* v : m_vars) vars.push_back(v); for (expr* e : m_lits) lits.push_back(e); flatten_and(lits); - qe::term_graph tg(m); - tg.set_vars(vars, false); - tg.add_lits(lits); - expr_ref_vector p = tg.project(); - ctx.regular_stream() << p << "\n"; + solver_factory& sf = ctx.get_solver_factory(); + params_ref pa; + solver_ref s = sf(m, pa, false, true, true, symbol::null); + solver_ref se = sf(m, pa, false, true, true, symbol::null); + s->assert_expr(lits); + lbool r = s->check_sat(); + if (r != l_true) { + ctx.regular_stream() << "sat check " << r << "\n"; + return; + } + model_ref mdl; + s->get_model(mdl); + qe::euf_arith_mbi_plugin plugin(s.get(), se.get()); + plugin.project(mdl, lits); + ctx.regular_stream() << lits << "\n"; } }; @@ -540,8 +532,6 @@ void install_dbg_cmds(cmd_context & ctx) { ctx.insert(alloc(set_cmd)); ctx.insert(alloc(pp_var_cmd)); ctx.insert(alloc(shift_vars_cmd)); - ctx.insert(alloc(pp_shared_cmd)); - ctx.insert(alloc(num_shared_cmd)); ctx.insert(alloc(assert_not_cmd)); ctx.insert(alloc(size_cmd)); ctx.insert(alloc(subst_cmd)); diff --git a/src/cmd_context/simplify_cmd.cpp b/src/cmd_context/simplify_cmd.cpp index de548562e..077a46659 100644 --- a/src/cmd_context/simplify_cmd.cpp +++ b/src/cmd_context/simplify_cmd.cpp @@ -24,29 +24,10 @@ Notes: #include "util/scoped_timer.h" #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" -#include "ast/rewriter/seq_rewriter.h" #include class simplify_cmd : public parametric_cmd { - class th_solver : public expr_solver { - cmd_context& m_ctx; - params_ref m_params; - ref m_solver; - public: - th_solver(cmd_context& ctx): m_ctx(ctx) {} - - lbool check_sat(expr* e) override { - if (!m_solver) { - m_solver = m_ctx.get_solver_factory()(m_ctx.m(), m_params, false, true, false, symbol::null); - } - m_solver->push(); - m_solver->assert_expr(e); - lbool r = m_solver->check_sat(0,nullptr); - m_solver->pop(1); - return r; - } - }; expr * m_target; public: diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index a89e76edb..7fdca8cdd 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -224,7 +224,7 @@ public: ctx.display_sat_result(r); result->set_status(r); if (r == l_undef) { - if (reason_unknown != "") { + if (!reason_unknown.empty()) { result->m_unknown = reason_unknown; // ctx.diagnostic_stream() << "\"" << escaped(reason_unknown.c_str(), true) << "\"" << std::endl; } diff --git a/src/math/polynomial/algebraic_numbers.cpp b/src/math/polynomial/algebraic_numbers.cpp index 17360f35b..25a1985a7 100644 --- a/src/math/polynomial/algebraic_numbers.cpp +++ b/src/math/polynomial/algebraic_numbers.cpp @@ -561,7 +561,9 @@ namespace algebraic_numbers { }; void sort_roots(numeral_vector & r) { - std::sort(r.begin(), r.end(), lt_proc(m_wrapper)); + if (m_limit.inc()) { + std::sort(r.begin(), r.end(), lt_proc(m_wrapper)); + } } void isolate_roots(scoped_upoly const & up, numeral_vector & roots) { @@ -1750,8 +1752,7 @@ namespace algebraic_numbers { // then they MUST BE DIFFERENT. // Thus, if we keep refining the interval of a and b, // eventually they will not overlap - while (true) { - checkpoint(); + while (m_limit.inc()) { refine(a); refine(b); m_compare_refine++; @@ -1764,6 +1765,9 @@ namespace algebraic_numbers { } } + if (!m_limit.inc()) + return 0; + // make sure that intervals of a and b have the same magnitude int a_m = magnitude(a_lower, a_upper); int b_m = magnitude(b_lower, b_upper); @@ -1810,6 +1814,7 @@ namespace algebraic_numbers { // V == 0 --> a = b // if (V < 0) == (p_b(b_lower) < 0) then b > a else b < a // + m_compare_sturm++; upolynomial::scoped_upolynomial_sequence seq(upm()); upm().sturm_tarski_seq(cell_a->m_p_sz, cell_a->m_p, cell_b->m_p_sz, cell_b->m_p, seq); diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index aec901f61..8f7f08c7f 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -67,7 +67,7 @@ namespace polynomial { bool first = true; out << "["; for (unsigned i = 0; i < m_var2degree.size(); ++ i) { - if (m_var2degree.size() > 0) { + if (!m_var2degree.empty()) { if (!first) { out << ","; } diff --git a/src/math/polynomial/upolynomial.cpp b/src/math/polynomial/upolynomial.cpp index 39bdb6812..65dbf91f4 100644 --- a/src/math/polynomial/upolynomial.cpp +++ b/src/math/polynomial/upolynomial.cpp @@ -96,7 +96,7 @@ namespace upolynomial { void core_manager::factors::display(std::ostream & out) const { out << nm().to_string(m_constant); - if (m_factors.size() > 0) { + if (!m_factors.empty()) { for (unsigned i = 0; i < m_factors.size(); ++ i) { out << " * ("; m_upm.display(out, m_factors[i]); @@ -524,11 +524,11 @@ namespace upolynomial { set(sz1, p1, buffer); if (sz1 <= 1) return; + numeral const & b_n = p2[sz2-1]; SASSERT(!m().is_zero(b_n)); scoped_numeral a_m(m()); - while (true) { - checkpoint(); + while (m_limit.inc()) { TRACE("rem_bug", tout << "rem loop, p2:\n"; display(tout, sz2, p2); tout << "\nbuffer:\n"; display(tout, buffer); tout << "\n";); sz1 = buffer.size(); if (sz1 < sz2) { @@ -1339,12 +1339,10 @@ namespace upolynomial { // Return the number of sign changes in the coefficients of p unsigned manager::sign_changes(unsigned sz, numeral const * p) { unsigned r = 0; - int sign, prev_sign; - sign = 0; - prev_sign = 0; + int prev_sign = 0; unsigned i = 0; for (; i < sz; i++) { - sign = sign_of(p[i]); + int sign = sign_of(p[i]); if (sign == 0) continue; if (sign != prev_sign && prev_sign != 0) @@ -2519,7 +2517,7 @@ namespace upolynomial { // Keep expanding the Sturm sequence starting at seq void manager::sturm_seq_core(upolynomial_sequence & seq) { scoped_numeral_vector r(m()); - while (true) { + while (m_limit.inc()) { unsigned sz = seq.size(); srem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); if (is_zero(r)) diff --git a/src/math/polynomial/upolynomial_factorization.cpp b/src/math/polynomial/upolynomial_factorization.cpp index 5d9d3f1f1..45cc3f86d 100644 --- a/src/math/polynomial/upolynomial_factorization.cpp +++ b/src/math/polynomial/upolynomial_factorization.cpp @@ -532,7 +532,7 @@ bool check_hansel_lift(z_manager & upm, numeral_vector const & C, upm.mul(A_lifted.size(), A_lifted.c_ptr(), B_lifted.size(), B_lifted.c_ptr(), test1); upm.sub(C.size(), C.c_ptr(), test1.size(), test1.c_ptr(), test1); to_zp_manager(br_upm, test1); - if (test1.size() != 0) { + if (!test1.empty()) { TRACE("polynomial::factorization::bughunt", tout << "sage: R. = ZZ['x']" << endl; tout << "sage: A = "; upm.display(tout, A); tout << endl; @@ -1072,7 +1072,8 @@ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, prime_iterator prime_it; scoped_numeral gcd_tmp(nm); unsigned trials = 0; - while (trials < params.m_p_trials) { + TRACE("polynomial::factorization::bughunt", tout << "trials: " << params.m_p_trials << "\n";); + while (trials <= params.m_p_trials) { upm.checkpoint(); // construct prime to check uint64_t next_prime = prime_it.next(); diff --git a/src/math/polynomial/upolynomial_factorization_int.h b/src/math/polynomial/upolynomial_factorization_int.h index 10bfb4d8b..816914545 100644 --- a/src/math/polynomial/upolynomial_factorization_int.h +++ b/src/math/polynomial/upolynomial_factorization_int.h @@ -401,7 +401,7 @@ namespace upolynomial { } else { if (selection_i >= m_current.size() || (int) current < m_current[selection_i]) { SASSERT(m_factors.get_degree(current) == 1); - if (out.size() == 0) { + if (out.empty()) { upm.set(m_factors[current].size(), m_factors[current].c_ptr(), out); } else { upm.mul(out.size(), out.c_ptr(), m_factors[current].size(), m_factors[current].c_ptr(), out); diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 77623a2df..3e05be734 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -154,7 +154,7 @@ namespace realclosure { struct value { unsigned m_ref_count; //!< Reference counter - bool m_rational; //!< True if the value is represented as an abitrary precision rational value. + bool m_rational; //!< True if the value is represented as an arbitrary precision rational value. mpbqi m_interval; //!< approximation as an interval with binary rational end-points // When performing an operation OP, we may have to make the width (upper - lower) of m_interval very small. // The precision (i.e., a small interval) needed for executing OP is usually unnecessary for subsequent operations, @@ -283,7 +283,7 @@ namespace realclosure { struct algebraic : public extension { polynomial m_p; mpbqi m_iso_interval; - sign_det * m_sign_det; //!< != 0 if m_iso_interval constains more than one root of m_p. + sign_det * m_sign_det; //!< != 0 if m_iso_interval constrains more than one root of m_p. unsigned m_sc_idx; //!< != UINT_MAX if m_sign_det != 0, in this case m_sc_idx < m_sign_det->m_sign_conditions.size() bool m_depends_on_infinitesimals; //!< True if the polynomial p depends on infinitesimal extensions. @@ -1741,7 +1741,7 @@ namespace realclosure { \brief In the sign determination algorithm main loop, we keep processing polynomials q, and checking whether they discriminate the roots of the target polynomial. - The vectors sc_cardinalities contains the cardinalites of the new realizable sign conditions. + The vectors sc_cardinalities contains the cardinalities of the new realizable sign conditions. That is, we started we a sequence of sign conditions sc_1, ..., sc_n, If q2_used is true, then we expanded this sequence as @@ -1750,7 +1750,7 @@ namespace realclosure { Thus, q is useful (i.e., it is a discriminator for the roots of p) IF If !q2_used, then There is an i s.t. sc_cardinalities[2*i] > 0 && sc_cardinalities[2*i] > 0 - If q2_used, then There is an i s.t. AtLeatTwo(sc_cardinalities[3*i] > 0, sc_cardinalities[3*i+1] > 0, sc_cardinalities[3*i+2] > 0) + If q2_used, then There is an i s.t. AtLeastTwo(sc_cardinalities[3*i] > 0, sc_cardinalities[3*i+1] > 0, sc_cardinalities[3*i+2] > 0) */ bool keep_new_sc_assignment(unsigned sz, int const * sc_cardinalities, bool q2_used) { SASSERT(q2_used || sz % 2 == 0); @@ -2038,7 +2038,7 @@ namespace realclosure { // We should keep q only if it discriminated something. // That is, // If !use_q2, then There is an i s.t. sc_cardinalities[2*i] > 0 && sc_cardinalities[2*i] > 0 - // If use_q2, then There is an i s.t. AtLeatTwo(sc_cardinalities[3*i] > 0, sc_cardinalities[3*i+1] > 0, sc_cardinalities[3*i+2] > 0) + // If use_q2, then There is an i s.t. AtLeastTwo(sc_cardinalities[3*i] > 0, sc_cardinalities[3*i+1] > 0, sc_cardinalities[3*i+2] > 0) if (!keep_new_sc_assignment(sc_cardinalities.size(), sc_cardinalities.c_ptr(), use_q2)) { // skip q since it did not reduced the cardinality of the existing sign conditions. continue; @@ -3466,11 +3466,11 @@ namespace realclosure { // --------------------------------- bool is_monic(value_ref_buffer const & p) { - return p.size() > 0 && is_rational_one(p[p.size() - 1]); + return !p.empty() && is_rational_one(p[p.size() - 1]); } bool is_monic(polynomial const & p) { - return p.size() > 0 && is_rational_one(p[p.size() - 1]); + return !p.empty() && is_rational_one(p[p.size() - 1]); } /** diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 8a771e07c..4053c41b7 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -606,7 +606,7 @@ namespace opt { } } - void model_based_opt::mk_coeffs_without(vector& dst, vector const src, unsigned x) { + void model_based_opt::mk_coeffs_without(vector& dst, vector const& src, unsigned x) { for (var const & v : src) { if (v.m_id != x) dst.push_back(v); } diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index e52d0cfe0..30442fc58 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -132,7 +132,7 @@ namespace opt { void normalize(unsigned row_id); - void mk_coeffs_without(vector& dst, vector const src, unsigned x); + void mk_coeffs_without(vector& dst, vector const& src, unsigned x); unsigned new_row(); diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 38eb4b00b..f981270b6 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -25,11 +25,11 @@ Revision History: func_entry::func_entry(ast_manager & m, unsigned arity, expr * const * args, expr * result): m_args_are_values(true), m_result(result) { - SASSERT(is_ground(result)); + //SASSERT(is_ground(result)); m.inc_ref(result); for (unsigned i = 0; i < arity; i++) { expr * arg = args[i]; - SASSERT(is_ground(arg)); + //SASSERT(is_ground(arg)); if (!m.is_value(arg)) m_args_are_values = false; m.inc_ref(arg); diff --git a/src/model/model.cpp b/src/model/model.cpp index 2efc39db8..e6a3ffedf 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -85,7 +85,7 @@ struct model::value_proc : public some_value_proc { expr * operator()(sort * s) override { ptr_vector * u = nullptr; if (m_model.m_usort2universe.find(s, u)) { - if (u->size() > 0) + if (!u->empty()) return u->get(0); } return nullptr; @@ -277,6 +277,13 @@ model::func_decl_set* model::collect_deps(top_sort& ts, func_interp * fi) { fi->compress(); expr* e = fi->get_else(); if (e) for_each_expr(collector, e); + unsigned num_args = fi->get_arity(); + for (func_entry* fe : *fi) { + for (unsigned i = 0; i < num_args; ++i) { + for_each_expr(collector, fe->get_arg(i)); + } + for_each_expr(collector, fe->get_result()); + } return s; } @@ -468,6 +475,10 @@ expr_ref model::operator()(expr* t) { return m_mev(t); } +void model::set_solver(expr_solver* s) { + m_mev.set_solver(s); +} + expr_ref_vector model::operator()(expr_ref_vector const& ts) { expr_ref_vector rs(m); for (expr* t : ts) rs.push_back((*this)(t)); diff --git a/src/model/model.h b/src/model/model.h index 0b74de771..2599a3fcd 100644 --- a/src/model/model.h +++ b/src/model/model.h @@ -95,6 +95,7 @@ public: bool is_false(expr* t); bool is_true(expr_ref_vector const& ts); void reset_eval_cache(); + void set_solver(expr_solver* solver); class scoped_model_completion { bool m_old_completion; diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 0fe1fe7c6..968aca51f 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -40,6 +40,7 @@ Revision History: struct evaluator_cfg : public default_rewriter_cfg { ast_manager & m; model_core & m_model; + params_ref m_params; bool_rewriter m_b_rw; arith_rewriter m_a_rw; bv_rewriter m_bv_rw; @@ -55,10 +56,13 @@ struct evaluator_cfg : public default_rewriter_cfg { bool m_cache; bool m_array_equalities; bool m_array_as_stores; + obj_map m_def_cache; + expr_ref_vector m_pinned; evaluator_cfg(ast_manager & m, model_core & md, params_ref const & p): m(m), m_model(md), + m_params(p), m_b_rw(m), // We must allow customers to set parameters for arithmetic rewriter/evaluator. // In particular, the maximum degree of algebraic numbers that will be evaluated. @@ -70,7 +74,8 @@ struct evaluator_cfg : public default_rewriter_cfg { m_pb_rw(m), m_f_rw(m), m_seq_rw(m), - m_ar(m) { + m_ar(m), + m_pinned(m) { bool flat = true; m_b_rw.set_flat(flat); m_a_rw.set_flat(flat); @@ -200,6 +205,39 @@ struct evaluator_cfg : public default_rewriter_cfg { return BR_REWRITE1; } } +#if 1 + if (st == BR_FAILED && num == 0 && m_ar.is_as_array(f) && m_model_completion) { + func_decl* g = nullptr; + VERIFY(m_ar.is_as_array(f, g)); + expr* def = nullptr; + quantifier* q = nullptr; + proof* def_pr = nullptr; + if (m_def_cache.find(g, def)) { + result = def; + return BR_DONE; + } + if (get_macro(g, def, q, def_pr)) { + sort_ref_vector sorts(m); + expr_ref_vector vars(m); + svector var_names; + unsigned sz = g->get_arity(); + for (unsigned i = 0; i < sz; ++i) { + var_names.push_back(symbol(sz - i - 1)); + vars.push_back(m.mk_var(sz - i - 1, g->get_domain(i))); + sorts.push_back(g->get_domain(i)); + } + var_subst subst(m, false); + result = subst(def, sorts.size(), vars.c_ptr()); + result = m.mk_lambda(sorts.size(), sorts.c_ptr(), var_names.c_ptr(), result); + model_evaluator ev(m_model, m_params); + result = ev(result); + m_pinned.push_back(result); + m_def_cache.insert(g, result); + return BR_DONE; + } + } +#endif + CTRACE("model_evaluator", st != BR_FAILED, tout << result << "\n";); return st; } @@ -222,7 +260,7 @@ struct evaluator_cfg : public default_rewriter_cfg { } } - bool get_macro(func_decl * f, expr * & def, quantifier * & q, proof * & def_pr) { + bool get_macro(func_decl * f, expr * & def, quantifier * & , proof * &) { #define TRACE_MACRO TRACE("model_evaluator", tout << "get_macro for " << f->get_name() << " (model completion: " << m_model_completion << ")\n";); @@ -627,3 +665,7 @@ bool model_evaluator::eval(expr_ref_vector const& ts, expr_ref& r, bool model_co tmp = mk_and(ts); return eval(tmp, r, model_completion); } + +void model_evaluator::set_solver(expr_solver* solver) { + m_imp->m_cfg.m_seq_rw.set_solver(solver); +} diff --git a/src/model/model_evaluator.h b/src/model/model_evaluator.h index 8666e3519..1959af246 100644 --- a/src/model/model_evaluator.h +++ b/src/model/model_evaluator.h @@ -25,6 +25,7 @@ Revision History: class model; class model_core; +class expr_solver; typedef rewriter_exception model_evaluator_exception; @@ -55,6 +56,8 @@ public: bool is_false(expr * t); bool is_true(expr_ref_vector const& ts); + void set_solver(expr_solver* solver); + /** * best effort evaluator of extensional array equality. */ diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 1c9abf78c..e8578f327 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -658,7 +658,7 @@ namespace datalog { void context::add_table_fact(func_decl * pred, unsigned num_args, unsigned args[]) { if (pred->get_arity() != num_args) { std::ostringstream out; - out << "miss-matched number of arguments passed to " << mk_ismt2_pp(pred, m) << " " << num_args << " passed"; + out << "mismatched number of arguments passed to " << mk_ismt2_pp(pred, m) << " " << num_args << " passed"; throw default_exception(out.str()); } table_fact fact; @@ -1243,7 +1243,7 @@ namespace datalog { void context::declare_vars(expr_ref_vector& rules, mk_fresh_name& fresh_names, std::ostream& out) { // // replace bound variables in rules by 'var declarations' - // First remove quantifers, then replace bound variables + // First remove quantifiers, then replace bound variables // by fresh constants. // smt2_pp_environment_dbg env(m); diff --git a/src/muz/base/dl_rule_set.h b/src/muz/base/dl_rule_set.h index e870e369c..633676ec7 100644 --- a/src/muz/base/dl_rule_set.h +++ b/src/muz/base/dl_rule_set.h @@ -230,7 +230,7 @@ namespace datalog { bool is_closed() const { return m_stratifier != 0; } unsigned get_num_rules() const { return m_rules.size(); } - bool empty() const { return m_rules.size() == 0; } + bool empty() const { return m_rules.empty(); } rule * get_rule(unsigned i) const { return m_rules[i]; } rule * last() const { return m_rules[m_rules.size()-1]; } diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index f804a239a..b03a9aab1 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -986,7 +986,7 @@ namespace datalog { m_sort2pred.insert(new_sorts[i].get(), it->m_key); m_pinned.push_back(new_sorts[i].get()); } - if (new_sorts.size() > 0) { + if (!new_sorts.empty()) { TRACE("bmc", dtu.display_datatype(new_sorts[0].get(), tout);); } del_datatype_decls(dts.size(), dts.c_ptr()); diff --git a/src/muz/fp/datalog_parser.cpp b/src/muz/fp/datalog_parser.cpp index 1cc85e6cd..0724d471a 100644 --- a/src/muz/fp/datalog_parser.cpp +++ b/src/muz/fp/datalog_parser.cpp @@ -101,7 +101,7 @@ public: resize_data(0); #if _WINDOWS errno_t err = fopen_s(&m_file, fname, "rb"); - m_ok = (m_file != NULL) && (err == 0); + m_ok = (m_file != nullptr) && (err == 0); #else m_file = fopen(fname, "rb"); m_ok = (m_file != nullptr); @@ -1057,7 +1057,7 @@ protected: line.push_back(ch); ch = strm.get(); } - return line.size() > 0; + return !line.empty(); } void add_rule(app* head, unsigned sz, app* const* body, const bool * is_neg) { diff --git a/src/muz/rel/dl_base.h b/src/muz/rel/dl_base.h index decf499a2..d307912be 100644 --- a/src/muz/rel/dl_base.h +++ b/src/muz/rel/dl_base.h @@ -1120,10 +1120,7 @@ namespace datalog { virtual bool operator==(const iterator_core & it) { //we worry about the equality operator only because of checking //the equality with the end() iterator - if(is_finished() && it.is_finished()) { - return true; - } - return false; + return is_finished() && it.is_finished(); } private: //private and undefined copy constructor and assignment operator @@ -1153,10 +1150,7 @@ namespace datalog { virtual bool operator==(const row_iterator_core & it) { //we worry about the equality operator only because of checking //the equality with the end() iterator - if(is_finished() && it.is_finished()) { - return true; - } - return false; + return is_finished() && it.is_finished(); } private: //private and undefined copy constructor and assignment operator diff --git a/src/muz/rel/dl_compiler.h b/src/muz/rel/dl_compiler.h index d7bdcad34..6bc2a819f 100644 --- a/src/muz/rel/dl_compiler.h +++ b/src/muz/rel/dl_compiler.h @@ -148,7 +148,7 @@ namespace datalog { void make_join(reg_idx t1, reg_idx t2, const variable_intersection & vars, reg_idx & result, bool reuse_t1, instruction_block & acc); void make_min(reg_idx source, reg_idx & target, const unsigned_vector & group_by_cols, - const unsigned min_col, instruction_block & acc); + unsigned min_col, instruction_block & acc); void make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, const unsigned_vector & removed_cols, reg_idx & result, bool reuse_t1, instruction_block & acc); void make_filter_interpreted_and_project(reg_idx src, app_ref & cond, diff --git a/src/muz/rel/dl_finite_product_relation.cpp b/src/muz/rel/dl_finite_product_relation.cpp index 0b1fbc840..ccb4584f7 100644 --- a/src/muz/rel/dl_finite_product_relation.cpp +++ b/src/muz/rel/dl_finite_product_relation.cpp @@ -487,7 +487,7 @@ namespace datalog { res->init(*res_table, joined_orelations, true); - if(m_tr_table_joined_cols.size()) { + if(!m_tr_table_joined_cols.empty()) { //There were some shared variables between the table and the relation part. //We enforce those equalities here. if(!m_filter_tr_identities) { diff --git a/src/muz/rel/dl_instruction.cpp b/src/muz/rel/dl_instruction.cpp index 2b76d99f7..79508a4f6 100644 --- a/src/muz/rel/dl_instruction.cpp +++ b/src/muz/rel/dl_instruction.cpp @@ -41,7 +41,6 @@ namespace datalog { execution_context::~execution_context() { reset(); - dealloc(m_stopwatch); } void execution_context::reset() { @@ -104,15 +103,15 @@ namespace datalog { m_timelimit_ms = time_in_ms; if (!m_stopwatch) { m_stopwatch = alloc(stopwatch); + } else { + m_stopwatch->stop(); + m_stopwatch->reset(); } - m_stopwatch->stop(); - m_stopwatch->reset(); m_stopwatch->start(); } void execution_context::reset_timelimit() { - if (m_stopwatch) { - m_stopwatch->stop(); - } + dealloc(m_stopwatch); + m_stopwatch = nullptr; m_timelimit_ms = 0; } diff --git a/src/muz/rel/dl_instruction.h b/src/muz/rel/dl_instruction.h index 6e0b7bfe5..918763a95 100644 --- a/src/muz/rel/dl_instruction.h +++ b/src/muz/rel/dl_instruction.h @@ -285,7 +285,7 @@ namespace datalog { const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, reg_idx result); static instruction * mk_min(reg_idx source, reg_idx target, const unsigned_vector & group_by_cols, - const unsigned min_col); + unsigned min_col); static instruction * mk_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle, reg_idx tgt); static instruction * mk_filter_by_negation(reg_idx tgt, reg_idx neg_rel, unsigned col_cnt, diff --git a/src/muz/rel/dl_sieve_relation.h b/src/muz/rel/dl_sieve_relation.h index 5f20cecb4..2d89faa5b 100644 --- a/src/muz/rel/dl_sieve_relation.h +++ b/src/muz/rel/dl_sieve_relation.h @@ -170,7 +170,7 @@ namespace datalog { SASSERT(is_inner_col(idx)); return m_sig2inner[idx]; } - bool no_sieved_columns() const { return m_ignored_cols.size()==0; } + bool no_sieved_columns() const { return m_ignored_cols.empty(); } bool no_inner_columns() const { return m_ignored_cols.size()==get_signature().size(); } relation_base & get_inner() { return *m_inner; } diff --git a/src/muz/rel/dl_sparse_table.cpp b/src/muz/rel/dl_sparse_table.cpp index a51fbf3b1..db62d14d5 100644 --- a/src/muz/rel/dl_sparse_table.cpp +++ b/src/muz/rel/dl_sparse_table.cpp @@ -1216,7 +1216,7 @@ namespace datalog { verbose_action _va("filter_by_negation"); - if (m_cols1.size() == 0) { + if (m_cols1.empty()) { if (!neg.empty()) { tgt.reset(); } diff --git a/src/muz/rel/dl_sparse_table.h b/src/muz/rel/dl_sparse_table.h index 43a967729..46f6f3932 100644 --- a/src/muz/rel/dl_sparse_table.h +++ b/src/muz/rel/dl_sparse_table.h @@ -72,7 +72,7 @@ namespace datalog { ~sparse_table_plugin() override; bool can_handle_signature(const table_signature & s) override - { return s.size()>0; } + { return !s.empty(); } table_base * mk_empty(const table_signature & s) override; sparse_table * mk_clone(const sparse_table & t); diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index bad25ef71..76e7e24c6 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -3058,7 +3058,7 @@ expr_ref context::get_ground_sat_answer() const { ground_fact_conjs.push_back(m.mk_eq(sig_arg, sig_val)); ground_arg_vals.push_back(sig_val); } - if (ground_fact_conjs.size () > 0) { + if (!ground_fact_conjs.empty()) { expr_ref ground_fact(m); ground_fact = mk_and(ground_fact_conjs); m_pm.formula_o2n(ground_fact, ground_fact, i); diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index 27b8ee357..f28864e27 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -27,443 +27,426 @@ Notes: #include "muz/spacer/spacer_iuc_proof.h" namespace spacer { -void iuc_solver::push () -{ - m_defs.push_back (def_manager (*this)); - m_solver.push (); -} - -void iuc_solver::pop (unsigned n) -{ - m_solver.pop (n); - unsigned lvl = m_defs.size (); - SASSERT (n <= lvl); - unsigned new_lvl = lvl-n; - while (m_defs.size() > new_lvl) { - m_num_proxies -= m_defs.back ().m_defs.size (); - m_defs.pop_back (); + void iuc_solver::push () { + m_defs.push_back (def_manager (*this)); + m_solver.push (); } -} - -app* iuc_solver::fresh_proxy () -{ - if (m_num_proxies == m_proxies.size()) { - std::stringstream name; - name << "spacer_proxy!" << m_proxies.size (); - app_ref res(m); - res = m.mk_const (symbol (name.str ().c_str ()), - m.mk_bool_sort ()); - m_proxies.push_back (res); - - // -- add the new proxy to proxy eliminator - proof_ref pr(m); - pr = m.mk_asserted (m.mk_true ()); - m_elim_proxies_sub.insert (res, m.mk_true (), pr); + void iuc_solver::pop (unsigned n) { + m_solver.pop (n); + unsigned lvl = m_defs.size (); + SASSERT (n <= lvl); + unsigned new_lvl = lvl-n; + while (m_defs.size() > new_lvl) { + m_num_proxies -= m_defs.back ().m_defs.size (); + m_defs.pop_back (); + } } - return m_proxies.get (m_num_proxies++); -} -app* iuc_solver::mk_proxy (expr *v) -{ - { + app* iuc_solver::fresh_proxy () { + if (m_num_proxies == m_proxies.size()) { + std::stringstream name; + name << "spacer_proxy!" << m_proxies.size (); + app_ref res(m); + res = m.mk_const (symbol (name.str ().c_str ()), + m.mk_bool_sort ()); + m_proxies.push_back (res); + + // -- add the new proxy to proxy eliminator + proof_ref pr(m); + pr = m.mk_asserted (m.mk_true ()); + m_elim_proxies_sub.insert (res, m.mk_true (), pr); + + } + return m_proxies.get (m_num_proxies++); + } + + app* iuc_solver::mk_proxy (expr *v) { expr *e = v; m.is_not (v, e); - if (is_uninterp_const(e)) { return to_app(v); } + if (is_uninterp_const(e)) { + return to_app(v); + } + + def_manager &def = !m_defs.empty() ? m_defs.back () : m_base_defs; + return def.mk_proxy (v); } - def_manager &def = m_defs.size () > 0 ? m_defs.back () : m_base_defs; - return def.mk_proxy (v); -} - -bool iuc_solver::mk_proxies (expr_ref_vector &v, unsigned from) -{ - bool dirty = false; - for (unsigned i = from, sz = v.size(); i < sz; ++i) { - app *p = mk_proxy (v.get (i)); - dirty |= (v.get (i) != p); - v[i] = p; + bool iuc_solver::mk_proxies (expr_ref_vector &v, unsigned from) { + bool dirty = false; + for (unsigned i = from, sz = v.size(); i < sz; ++i) { + app *p = mk_proxy (v.get (i)); + dirty |= (v.get (i) != p); + v[i] = p; + } + return dirty; } - return dirty; -} -void iuc_solver::push_bg (expr *e) -{ - if (m_assumptions.size () > m_first_assumption) - { m_assumptions.shrink(m_first_assumption); } - m_assumptions.push_back (e); - m_first_assumption = m_assumptions.size (); -} - -void iuc_solver::pop_bg (unsigned n) -{ - if (n == 0) { return; } - - if (m_assumptions.size () > m_first_assumption) { + void iuc_solver::push_bg (expr *e) { + if (m_assumptions.size () > m_first_assumption) { + m_assumptions.shrink(m_first_assumption); + } + m_assumptions.push_back (e); + m_first_assumption = m_assumptions.size (); + } + + void iuc_solver::pop_bg (unsigned n) { + if (n == 0) return; + + if (m_assumptions.size () > m_first_assumption) { + m_assumptions.shrink(m_first_assumption); + } + m_first_assumption = m_first_assumption > n ? m_first_assumption - n : 0; + m_assumptions.shrink (m_first_assumption); + } + + unsigned iuc_solver::get_num_bg () { + return m_first_assumption; + } + + lbool iuc_solver::check_sat_core (unsigned num_assumptions, expr * const *assumptions) { + // -- remove any old assumptions m_assumptions.shrink(m_first_assumption); + + // -- replace theory literals in background assumptions with proxies + mk_proxies (m_assumptions); + // -- in case mk_proxies added new literals, they are all background + m_first_assumption = m_assumptions.size (); + + m_assumptions.append (num_assumptions, assumptions); + m_is_proxied = mk_proxies (m_assumptions, m_first_assumption); + + return set_status (m_solver.check_sat (m_assumptions)); } - m_first_assumption = m_first_assumption > n ? m_first_assumption - n : 0; - m_assumptions.shrink (m_first_assumption); -} + + lbool iuc_solver::check_sat_cc(const expr_ref_vector &cube, + vector const & clauses) { + if (clauses.empty()) + return check_sat(cube.size(), cube.c_ptr()); + + // -- remove any old assumptions + m_assumptions.shrink(m_first_assumption); + + // -- replace theory literals in background assumptions with proxies + mk_proxies(m_assumptions); + // -- in case mk_proxies added new literals, they are all background + m_first_assumption = m_assumptions.size(); + + m_assumptions.append(cube); + m_is_proxied = mk_proxies(m_assumptions, m_first_assumption); + + return set_status (m_solver.check_sat_cc(m_assumptions, clauses)); + } + -unsigned iuc_solver::get_num_bg () {return m_first_assumption;} - -lbool iuc_solver::check_sat (unsigned num_assumptions, expr * const *assumptions) -{ - // -- remove any old assumptions - m_assumptions.shrink(m_first_assumption); - - // -- replace theory literals in background assumptions with proxies - mk_proxies (m_assumptions); - // -- in case mk_proxies added new literals, they are all background - m_first_assumption = m_assumptions.size (); - - m_assumptions.append (num_assumptions, assumptions); - m_is_proxied = mk_proxies (m_assumptions, m_first_assumption); - - return set_status (m_solver.check_sat (m_assumptions)); -} - -lbool iuc_solver::check_sat_cc(const expr_ref_vector &cube, - vector const & clauses) { - if (clauses.empty()) - return check_sat(cube.size(), cube.c_ptr()); - - // -- remove any old assumptions - m_assumptions.shrink(m_first_assumption); - - // -- replace theory literals in background assumptions with proxies - mk_proxies(m_assumptions); - // -- in case mk_proxies added new literals, they are all background - m_first_assumption = m_assumptions.size(); - - m_assumptions.append(cube); - m_is_proxied = mk_proxies(m_assumptions, m_first_assumption); - - return set_status (m_solver.check_sat_cc(m_assumptions, clauses)); -} - - -app* iuc_solver::def_manager::mk_proxy (expr *v) -{ - app* r; - if (m_expr2proxy.find(v, r)) - return r; - - ast_manager &m = m_parent.m; - app* proxy = m_parent.fresh_proxy (); - app* def = m.mk_or (m.mk_not (proxy), v); - m_defs.push_back (def); - m_expr2proxy.insert (v, proxy); - m_proxy2def.insert (proxy, def); - - m_parent.assert_expr (def); - return proxy; -} - -bool iuc_solver::def_manager::is_proxy (app *k, app_ref &def) -{ - app *r = nullptr; - bool found = m_proxy2def.find (k, r); - def = r; - return found; -} - -void iuc_solver::def_manager::reset () -{ - m_expr2proxy.reset (); - m_proxy2def.reset (); - m_defs.reset (); -} - -bool iuc_solver::def_manager::is_proxy_def (expr *v) -{ - // XXX This might not be the most robust way to check - return m_defs.contains (v); -} - -bool iuc_solver::is_proxy(expr *e, app_ref &def) -{ - if (!is_uninterp_const(e)) - return false; - - app* a = to_app (e); - - for (int i = m_defs.size (); i-- > 0; ) - if (m_defs[i].is_proxy (a, def)) - return true; - - return m_base_defs.is_proxy (a, def); -} - -void iuc_solver::collect_statistics (statistics &st) const -{ - m_solver.collect_statistics (st); - st.update ("time.iuc_solver.get_iuc", m_iuc_sw.get_seconds()); - st.update ("time.iuc_solver.get_iuc.hyp_reduce1", m_hyp_reduce1_sw.get_seconds()); - st.update ("time.iuc_solver.get_iuc.hyp_reduce2", m_hyp_reduce2_sw.get_seconds()); - st.update ("time.iuc_solver.get_iuc.learn_core", m_learn_core_sw.get_seconds()); - - st.update("iuc_solver.num_proxies", m_proxies.size()); -} - -void iuc_solver::reset_statistics () -{ - m_iuc_sw.reset(); - m_hyp_reduce1_sw.reset(); - m_hyp_reduce2_sw.reset(); - m_learn_core_sw.reset(); -} - -void iuc_solver::get_unsat_core (expr_ref_vector &core) { - m_solver.get_unsat_core (core); - undo_proxies_in_core (core); -} - -void iuc_solver::undo_proxies_in_core (expr_ref_vector &r) -{ - app_ref e(m); - expr_fast_mark1 bg; - for (unsigned i = 0; i < m_first_assumption; ++i) { - bg.mark(m_assumptions.get(i)); + app* iuc_solver::def_manager::mk_proxy (expr *v) { + app* r; + if (m_expr2proxy.find(v, r)) + return r; + + ast_manager &m = m_parent.m; + app* proxy = m_parent.fresh_proxy (); + app* def = m.mk_or (m.mk_not (proxy), v); + m_defs.push_back (def); + m_expr2proxy.insert (v, proxy); + m_proxy2def.insert (proxy, def); + + m_parent.assert_expr (def); + return proxy; } - // expand proxies - unsigned j = 0; - for (expr* rr : r) { - // skip background assumptions - if (bg.is_marked(rr)) - continue; + bool iuc_solver::def_manager::is_proxy (app *k, app_ref &def) { + app *r = nullptr; + bool found = m_proxy2def.find (k, r); + def = r; + return found; + } + + void iuc_solver::def_manager::reset () { + m_expr2proxy.reset (); + m_proxy2def.reset (); + m_defs.reset (); + } - // -- undo proxies, but only if they were introduced in check_sat - if (m_is_proxied && is_proxy(rr, e)) { - SASSERT (m.is_or (e)); - r[j++] = e->get_arg (1); + bool iuc_solver::def_manager::is_proxy_def (expr *v) { + // XXX This might not be the most robust way to check + return m_defs.contains (v); + } + + bool iuc_solver::is_proxy(expr *e, app_ref &def) { + if (!is_uninterp_const(e)) + return false; + + app* a = to_app (e); + + for (int i = m_defs.size (); i-- > 0; ) + if (m_defs[i].is_proxy (a, def)) + return true; + + return m_base_defs.is_proxy (a, def); + } + + void iuc_solver::collect_statistics (statistics &st) const { + m_solver.collect_statistics (st); + st.update ("time.iuc_solver.get_iuc", m_iuc_sw.get_seconds()); + st.update ("time.iuc_solver.get_iuc.hyp_reduce1", m_hyp_reduce1_sw.get_seconds()); + st.update ("time.iuc_solver.get_iuc.hyp_reduce2", m_hyp_reduce2_sw.get_seconds()); + st.update ("time.iuc_solver.get_iuc.learn_core", m_learn_core_sw.get_seconds()); + + st.update("iuc_solver.num_proxies", m_proxies.size()); + } + + void iuc_solver::reset_statistics () { + m_iuc_sw.reset(); + m_hyp_reduce1_sw.reset(); + m_hyp_reduce2_sw.reset(); + m_learn_core_sw.reset(); + } + + void iuc_solver::get_unsat_core (expr_ref_vector &core) { + m_solver.get_unsat_core (core); + undo_proxies_in_core (core); + } + + void iuc_solver::undo_proxies_in_core (expr_ref_vector &r) { + app_ref e(m); + expr_fast_mark1 bg; + for (unsigned i = 0; i < m_first_assumption; ++i) { + bg.mark(m_assumptions.get(i)); + } + + // expand proxies + unsigned j = 0; + for (expr* rr : r) { + // skip background assumptions + if (bg.is_marked(rr)) + continue; + + // -- undo proxies, but only if they were introduced in check_sat + if (m_is_proxied && is_proxy(rr, e)) { + SASSERT (m.is_or (e)); + r[j++] = e->get_arg (1); + } + else { + r[j++] = rr; + } + } + r.shrink (j); + } + + void iuc_solver::undo_proxies (expr_ref_vector &r) { + app_ref e(m); + // expand proxies + for (unsigned i = 0, sz = r.size (); i < sz; ++i) + if (is_proxy(r.get(i), e)) { + SASSERT (m.is_or (e)); + r[i] = e->get_arg (1); + } + } + + void iuc_solver::elim_proxies (expr_ref_vector &v) { + expr_ref f = mk_and (v); + scoped_ptr rep = mk_expr_simp_replacer (m); + rep->set_substitution (&m_elim_proxies_sub); + (*rep)(f); + v.reset(); + flatten_and(f, v); + } + + void iuc_solver::get_iuc(expr_ref_vector &core) { + scoped_watch _t_ (m_iuc_sw); + + typedef obj_hashtable expr_set; + expr_set core_lits; + for (unsigned i = m_first_assumption, sz = m_assumptions.size(); i < sz; ++i) { + expr *a = m_assumptions.get (i); + app_ref def(m); + if (is_proxy(a, def)) { core_lits.insert(def.get()); } + core_lits.insert (a); + } + + if (m_iuc == 0) { + // ORIGINAL PDR CODE + // AG: deprecated + proof_ref pr(m); + pr = get_proof (); + + farkas_learner learner_old; + learner_old.set_split_literals(m_split_literals); + + learner_old.get_lemmas (pr, core_lits, core); + elim_proxies (core); + simplify_bounds (core); // XXX potentially redundant } else { - r[j++] = rr; - } - } - r.shrink (j); -} - -void iuc_solver::undo_proxies (expr_ref_vector &r) -{ - app_ref e(m); - // expand proxies - for (unsigned i = 0, sz = r.size (); i < sz; ++i) - if (is_proxy(r.get(i), e)) { - SASSERT (m.is_or (e)); - r[i] = e->get_arg (1); - } -} - -void iuc_solver::elim_proxies (expr_ref_vector &v) -{ - expr_ref f = mk_and (v); - scoped_ptr rep = mk_expr_simp_replacer (m); - rep->set_substitution (&m_elim_proxies_sub); - (*rep)(f); - v.reset(); - flatten_and(f, v); -} - -void iuc_solver::get_iuc(expr_ref_vector &core) -{ - scoped_watch _t_ (m_iuc_sw); - - typedef obj_hashtable expr_set; - expr_set core_lits; - for (unsigned i = m_first_assumption, sz = m_assumptions.size(); i < sz; ++i) { - expr *a = m_assumptions.get (i); - app_ref def(m); - if (is_proxy(a, def)) { core_lits.insert(def.get()); } - core_lits.insert (a); - } - - if (m_iuc == 0) { - // ORIGINAL PDR CODE - // AG: deprecated - proof_ref pr(m); - pr = get_proof (); - - farkas_learner learner_old; - learner_old.set_split_literals(m_split_literals); - - learner_old.get_lemmas (pr, core_lits, core); - elim_proxies (core); - simplify_bounds (core); // XXX potentially redundant - } - else { - // NEW IUC - proof_ref res(get_proof(), m); - - // -- old hypothesis reducer while the new one is broken - if (m_old_hyp_reducer) { - scoped_watch _t_ (m_hyp_reduce1_sw); - // AG: deprecated - // pre-process proof in order to get a proof which is - // better suited for unsat-core-extraction - if (m_print_farkas_stats) { - iuc_proof iuc_before(m, res.get(), core_lits); - verbose_stream() << "\nOld reduce_hypotheses. Before:"; - iuc_before.dump_farkas_stats(); + // NEW IUC + proof_ref res(get_proof(), m); + + // -- old hypothesis reducer while the new one is broken + if (m_old_hyp_reducer) { + scoped_watch _t_ (m_hyp_reduce1_sw); + // AG: deprecated + // pre-process proof in order to get a proof which is + // better suited for unsat-core-extraction + if (m_print_farkas_stats) { + iuc_proof iuc_before(m, res.get(), core_lits); + verbose_stream() << "\nOld reduce_hypotheses. Before:"; + iuc_before.dump_farkas_stats(); + } + + proof_utils::reduce_hypotheses(res); + proof_utils::permute_unit_resolution(res); + + if (m_print_farkas_stats) { + iuc_proof iuc_after(m, res.get(), core_lits); + verbose_stream() << "Old reduce_hypothesis. After:"; + iuc_after.dump_farkas_stats(); + } } - - proof_utils::reduce_hypotheses(res); - proof_utils::permute_unit_resolution(res); - - if (m_print_farkas_stats) { - iuc_proof iuc_after(m, res.get(), core_lits); - verbose_stream() << "Old reduce_hypothesis. After:"; - iuc_after.dump_farkas_stats(); - } - } - // -- new hypothesis reducer - else - { + // -- new hypothesis reducer + else + { #if 0 - static unsigned bcnt = 0; + static unsigned bcnt = 0; + { + bcnt++; + TRACE("spacer", tout << "Dumping pf bcnt: " << bcnt << "\n";); + if (bcnt == 123) { + std::ofstream ofs; + ofs.open("/tmp/bpf_" + std::to_string(bcnt) + ".dot"); + iuc_proof iuc_pf_before(m, res.get(), core_lits); + iuc_pf_before.display_dot(ofs); + ofs.close(); + + proof_checker pc(m); + expr_ref_vector side(m); + ENSURE(pc.check(res, side)); + } + } +#endif + scoped_watch _t_ (m_hyp_reduce2_sw); + + // pre-process proof for better iuc extraction + if (m_print_farkas_stats) { + iuc_proof iuc_before(m, res.get(), core_lits); + verbose_stream() << "\n New hypothesis_reducer. Before:"; + iuc_before.dump_farkas_stats(); + } + + proof_ref pr1(m); + { + scoped_watch _t_ (m_hyp_reduce1_sw); + theory_axiom_reducer ta_reducer(m); + pr1 = ta_reducer.reduce (res.get()); + } + + proof_ref pr2(m); + { + scoped_watch _t_ (m_hyp_reduce2_sw); + hypothesis_reducer hyp_reducer(m); + pr2 = hyp_reducer.reduce(pr1); + } + + res = pr2; + + if (m_print_farkas_stats) { + iuc_proof iuc_after(m, res.get(), core_lits); + verbose_stream() << "New hypothesis_reducer. After:"; + iuc_after.dump_farkas_stats(); + } + } + + iuc_proof iuc_pf(m, res, core_lits); + +#if 0 + static unsigned cnt = 0; { - bcnt++; - TRACE("spacer", tout << "Dumping pf bcnt: " << bcnt << "\n";); - if (bcnt == 123) { + cnt++; + TRACE("spacer", tout << "Dumping pf cnt: " << cnt << "\n";); + if (cnt == 123) { std::ofstream ofs; - ofs.open("/tmp/bpf_" + std::to_string(bcnt) + ".dot"); - iuc_proof iuc_pf_before(m, res.get(), core_lits); - iuc_pf_before.display_dot(ofs); + ofs.open("/tmp/pf_" + std::to_string(cnt) + ".dot"); + iuc_pf.display_dot(ofs); ofs.close(); - proof_checker pc(m); expr_ref_vector side(m); ENSURE(pc.check(res, side)); } } #endif - scoped_watch _t_ (m_hyp_reduce2_sw); - - // pre-process proof for better iuc extraction - if (m_print_farkas_stats) { - iuc_proof iuc_before(m, res.get(), core_lits); - verbose_stream() << "\n New hypothesis_reducer. Before:"; - iuc_before.dump_farkas_stats(); + unsat_core_learner learner(m, iuc_pf); + + unsat_core_plugin* plugin; + // -- register iuc plugins + switch (m_iuc_arith) { + case 0: + case 1: + plugin = + alloc(unsat_core_plugin_farkas_lemma, + learner, m_split_literals, + (m_iuc_arith == 1) /* use constants from A */); + learner.register_plugin(plugin); + break; + case 2: + SASSERT(false && "Broken"); + plugin = alloc(unsat_core_plugin_farkas_lemma_optimized, learner, m); + learner.register_plugin(plugin); + break; + case 3: + plugin = alloc(unsat_core_plugin_farkas_lemma_bounded, learner, m); + learner.register_plugin(plugin); + break; + default: + UNREACHABLE(); + break; } - - proof_ref pr1(m); + + switch (m_iuc) { + case 1: + // -- iuc based on the lowest cut in the proof + plugin = alloc(unsat_core_plugin_lemma, learner); + learner.register_plugin(plugin); + break; + case 2: + // -- iuc based on the smallest cut in the proof + plugin = alloc(unsat_core_plugin_min_cut, learner, m); + learner.register_plugin(plugin); + break; + default: + UNREACHABLE(); + break; + } + { - scoped_watch _t_ (m_hyp_reduce1_sw); - theory_axiom_reducer ta_reducer(m); - pr1 = ta_reducer.reduce (res.get()); - } - - proof_ref pr2(m); - { - scoped_watch _t_ (m_hyp_reduce2_sw); - hypothesis_reducer hyp_reducer(m); - pr2 = hyp_reducer.reduce(pr1); - } - - res = pr2; - - if (m_print_farkas_stats) { - iuc_proof iuc_after(m, res.get(), core_lits); - verbose_stream() << "New hypothesis_reducer. After:"; - iuc_after.dump_farkas_stats(); + scoped_watch _t_ (m_learn_core_sw); + // compute interpolating unsat core + learner.compute_unsat_core(core); } + + elim_proxies (core); + // AG: this should be taken care of by minimizing the iuc cut + simplify_bounds (core); } - - iuc_proof iuc_pf(m, res, core_lits); - -#if 0 - static unsigned cnt = 0; - { - cnt++; - TRACE("spacer", tout << "Dumping pf cnt: " << cnt << "\n";); - if (cnt == 123) { - std::ofstream ofs; - ofs.open("/tmp/pf_" + std::to_string(cnt) + ".dot"); - iuc_pf.display_dot(ofs); - ofs.close(); - proof_checker pc(m); - expr_ref_vector side(m); - ENSURE(pc.check(res, side)); - } - } -#endif - unsat_core_learner learner(m, iuc_pf); - - unsat_core_plugin* plugin; - // -- register iuc plugins - switch (m_iuc_arith) { - case 0: - case 1: - plugin = - alloc(unsat_core_plugin_farkas_lemma, - learner, m_split_literals, - (m_iuc_arith == 1) /* use constants from A */); - learner.register_plugin(plugin); - break; - case 2: - SASSERT(false && "Broken"); - plugin = alloc(unsat_core_plugin_farkas_lemma_optimized, learner, m); - learner.register_plugin(plugin); - break; - case 3: - plugin = alloc(unsat_core_plugin_farkas_lemma_bounded, learner, m); - learner.register_plugin(plugin); - break; - default: - UNREACHABLE(); - break; - } - - switch (m_iuc) { - case 1: - // -- iuc based on the lowest cut in the proof - plugin = alloc(unsat_core_plugin_lemma, learner); - learner.register_plugin(plugin); - break; - case 2: - // -- iuc based on the smallest cut in the proof - plugin = alloc(unsat_core_plugin_min_cut, learner, m); - learner.register_plugin(plugin); - break; - default: - UNREACHABLE(); - break; - } - - { - scoped_watch _t_ (m_learn_core_sw); - // compute interpolating unsat core - learner.compute_unsat_core(core); - } - - elim_proxies (core); - // AG: this should be taken care of by minimizing the iuc cut - simplify_bounds (core); + + IF_VERBOSE(2, + verbose_stream () << "IUC Core:\n" << core << "\n";); } - - IF_VERBOSE(2, - verbose_stream () << "IUC Core:\n" << core << "\n";); -} - -void iuc_solver::refresh () -{ - // only refresh in non-pushed state - SASSERT (m_defs.empty()); - expr_ref_vector assertions (m); - for (unsigned i = 0, e = m_solver.get_num_assertions(); i < e; ++i) { - expr* a = m_solver.get_assertion (i); - if (!m_base_defs.is_proxy_def(a)) { assertions.push_back(a); } - + + void iuc_solver::refresh () { + // only refresh in non-pushed state + SASSERT (m_defs.empty()); + expr_ref_vector assertions (m); + for (unsigned i = 0, e = m_solver.get_num_assertions(); i < e; ++i) { + expr* a = m_solver.get_assertion (i); + if (!m_base_defs.is_proxy_def(a)) { assertions.push_back(a); } + + } + m_base_defs.reset (); + NOT_IMPLEMENTED_YET (); + // solver interface does not have a reset method. need to introduce it somewhere. + // m_solver.reset (); + for (unsigned i = 0, e = assertions.size (); i < e; ++i) + { m_solver.assert_expr(assertions.get(i)); } } - m_base_defs.reset (); - NOT_IMPLEMENTED_YET (); - // solver interface does not have a reset method. need to introduce it somewhere. - // m_solver.reset (); - for (unsigned i = 0, e = assertions.size (); i < e; ++i) - { m_solver.assert_expr(assertions.get(i)); } -} - + } diff --git a/src/muz/spacer/spacer_iuc_solver.h b/src/muz/spacer/spacer_iuc_solver.h index c0a0072ed..9b50b4c4e 100644 --- a/src/muz/spacer/spacer_iuc_solver.h +++ b/src/muz/spacer/spacer_iuc_solver.h @@ -122,12 +122,15 @@ public: void assert_expr_core(expr *t) override { m_solver.assert_expr(t); } void assert_expr_core2(expr *t, expr *a) override { NOT_IMPLEMENTED_YET(); } expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } + void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { m_solver.get_levels(vars, depth); } + expr_ref_vector get_trail() override { return m_solver.get_trail(); } + void set_activity(expr* lit, double act) override { m_solver.set_activity(lit, act); } void push() override; void pop(unsigned n) override; unsigned get_scope_level() const override { return m_solver.get_scope_level(); } - lbool check_sat(unsigned num_assumptions, expr * const *assumptions) override; + lbool check_sat_core(unsigned num_assumptions, expr * const *assumptions) override; lbool check_sat_cc(const expr_ref_vector &cube, vector const & clauses) override; void set_progress_callback(progress_callback *callback) override { m_solver.set_progress_callback(callback); diff --git a/src/muz/transforms/dl_mk_array_instantiation.cpp b/src/muz/transforms/dl_mk_array_instantiation.cpp index 626109528..a94383453 100644 --- a/src/muz/transforms/dl_mk_array_instantiation.cpp +++ b/src/muz/transforms/dl_mk_array_instantiation.cpp @@ -253,7 +253,7 @@ namespace datalog { all_selects.push_back(rewrite_select(array, select_ops[i])); } } - if(all_selects.size()==0) + if(all_selects.empty()) { expr_ref_vector dummy_args(m); dummy_args.push_back(array); diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index f430f3a0c..6ea764a0a 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -190,7 +190,7 @@ namespace nlsat { } ~imp() { - reset(); + clear(); } void mk_true_bvar() { @@ -230,6 +230,14 @@ namespace nlsat { m_assignment.reset(); } + void clear() { + m_explain.reset(); + m_lemma.reset(); + m_lazy_clause.reset(); + undo_until_size(0); + del_clauses(); + del_unref_atoms(); + } void checkpoint() { if (!m_rlimit.inc()) throw solver_exception(m_rlimit.get_cancel_msg()); diff --git a/src/opt/CMakeLists.txt b/src/opt/CMakeLists.txt index dcb13c062..fb61c0c33 100644 --- a/src/opt/CMakeLists.txt +++ b/src/opt/CMakeLists.txt @@ -1,5 +1,6 @@ z3_add_component(opt SOURCES + maxlex.cpp maxres.cpp maxsmt.cpp opt_cmds.cpp diff --git a/src/opt/maxlex.cpp b/src/opt/maxlex.cpp new file mode 100644 index 000000000..56dbfebf5 --- /dev/null +++ b/src/opt/maxlex.cpp @@ -0,0 +1,198 @@ +/*++ +Copyright (c) 2019 Microsoft Corporation + +Module Name: + + maxlex.cpp + +Abstract: + + MaxLex solves weighted max-sat problems where weights impose lexicographic order. + MaxSAT is particularly easy for this class: + In order of highest weight, check if soft constraint can be satisfied. + If so, assert it, otherwise assert the negation and record whether the soft + constraint is true or false in the solution. + +Author: + + Nikolaj Bjorner (nbjorner) 2019-25-1 + +--*/ + +#include "opt/opt_context.h" +#include "opt/maxsmt.h" +#include "opt/maxlex.h" + +namespace opt { + + bool is_maxlex(weights_t & _ws) { + vector ws(_ws); + std::sort(ws.begin(), ws.end()); + ws.reverse(); + rational sum(0); + for (rational const& w : ws) { + sum += w; + } + for (rational const& w : ws) { + if (sum > w + w) return false; + sum -= w; + } + return true; + } + + class maxlex : public maxsmt_solver_base { + + struct cmp_soft { + bool operator()(soft const& s1, soft const& s2) const { + return s1.weight > s2.weight; + } + }; + + ast_manager& m; + maxsat_context& m_c; + + void update_assignment() { + model_ref mdl; + s().get_model(mdl); + if (mdl) { + m_model = mdl; + m_c.model_updated(mdl.get()); + update_assignment(mdl); + } + } + + void assert_value(soft& soft) { + switch (soft.value) { + case l_true: + s().assert_expr(soft.s); + break; + case l_false: + s().assert_expr(expr_ref(m.mk_not(soft.s), m)); + break; + default: + break; + } + } + + void update_assignment(model_ref & mdl) { + bool first_undef = true, second_undef = false; + for (auto & soft : m_soft) { + if (first_undef && soft.value != l_undef) { + continue; + } + first_undef = false; + if (soft.value != l_false) { + lbool v = mdl->is_true(soft.s) ? l_true : l_undef;; + if (v == l_undef) { + second_undef = true; + } + if (second_undef) { + soft.set_value(v); + } + else { + SASSERT(v == l_true); + soft.set_value(v); + assert_value(soft); + } + } + } + update_bounds(); + } + + void update_bounds() { + m_lower.reset(); + m_upper.reset(); + for (auto & soft : m_soft) { + switch (soft.value) { + case l_undef: + m_upper += soft.weight; + break; + case l_true: + break; + case l_false: + m_lower += soft.weight; + m_upper += soft.weight; + break; + } + } + trace_bounds("maxlex"); + } + + void init() { + for (auto & soft : m_soft) { + soft.set_value(l_undef); + } + model_ref mdl; + s().get_model(mdl); + if (mdl) update_assignment(mdl); + } + + // + // include soft constraints that are known to be assignable to true after failed literal. + // + lbool maxlexN() { + unsigned sz = m_soft.size(); + for (unsigned i = 0; i < sz; ++i) { + auto& soft = m_soft[i]; + if (soft.value != l_undef) { + continue; + } + expr_ref_vector asms(m); + asms.push_back(soft.s); + lbool is_sat = s().check_sat(asms); + switch (is_sat) { + case l_true: + update_assignment(); + SASSERT(soft.value == l_true); + break; + case l_false: + soft.set_value(l_false); + assert_value(soft); + for (unsigned j = i + 1; j < sz; ++j) { + auto& soft2 = m_soft[j]; + if (soft2.value != l_true) { + break; + } + assert_value(soft2); + } + update_bounds(); + break; + case l_undef: + return l_undef; + } + } + return l_true; + } + + public: + + maxlex(maxsat_context& c, unsigned id, weights_t & ws, expr_ref_vector const& s): + maxsmt_solver_base(c, ws, s), + m(c.get_manager()), + m_c(c) { + // ensure that soft constraints are sorted with largest soft constraints first. + cmp_soft cmp; + std::sort(m_soft.begin(), m_soft.end(), cmp); + } + + lbool operator()() override { + init(); + return maxlexN(); + } + + + void commit_assignment() override { + for (auto & soft : m_soft) { + if (soft.value == l_undef) { + return; + } + assert_value(soft); + } + } + }; + + maxsmt_solver_base* mk_maxlex(maxsat_context& c, unsigned id, weights_t & ws, expr_ref_vector const& soft) { + return alloc(maxlex, c, id, ws, soft); + } + +} diff --git a/src/opt/maxlex.h b/src/opt/maxlex.h new file mode 100644 index 000000000..55aab76d3 --- /dev/null +++ b/src/opt/maxlex.h @@ -0,0 +1,32 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + maxlex.h + +Abstract: + + MaxLex solves weighted max-sat problems where weights impose lexicographic order. + +Author: + + Nikolaj Bjorner (nbjorner) 2019-25-1 + +Notes: + +--*/ + +#ifndef MAXLEX_H_ +#define MAXLEX_H_ + +namespace opt { + + bool is_maxlex(weights_t & ws); + + maxsmt_solver_base* mk_maxlex(maxsat_context& c, unsigned id, weights_t & ws, expr_ref_vector const& soft); + + +}; + +#endif diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp index 75a42886c..25b51372e 100644 --- a/src/opt/maxres.cpp +++ b/src/opt/maxres.cpp @@ -177,6 +177,7 @@ public: else { asum = mk_fresh_bool("soft"); fml = m.mk_iff(asum, e); + m_defs.push_back(fml); add(fml); } new_assumption(asum, w); @@ -328,8 +329,8 @@ public: verify_assumptions(); m_lower.reset(); for (soft& s : m_soft) { - s.is_true = m_model->is_true(s.s); - if (!s.is_true) { + s.set_value(m_model->is_true(s.s)); + if (!s.is_true()) { m_lower += s.weight; } } @@ -374,7 +375,7 @@ public: get_mus_model(mdl); is_sat = minimize_core(_core); core.append(_core.size(), _core.c_ptr()); - verify_core(core); + DEBUG_CODE(verify_core(core);); ++m_stats.m_num_cores; if (is_sat != l_true) { IF_VERBOSE(100, verbose_stream() << "(opt.maxres minimization failed)\n";); @@ -382,6 +383,7 @@ public: } if (core.empty()) { IF_VERBOSE(100, verbose_stream() << "(opt.maxres core is empty)\n";); + TRACE("opt", tout << "empty core\n";); cores.reset(); m_lower = m_upper; return l_true; @@ -516,6 +518,10 @@ public: max_resolve(core, w); fml = mk_not(m, mk_and(m, core.size(), core.c_ptr())); add(fml); + // save small cores such that lex-combinations of maxres can reuse these cores. + if (core.size() <= 2) { + m_defs.push_back(fml); + } m_lower += w; if (m_st == s_primal_dual) { m_lower = std::min(m_lower, m_upper); @@ -698,8 +704,7 @@ public: fml = m.mk_implies(d, cls); update_model(d, cls); add(fml); - m_defs.push_back(fml); - + m_defs.push_back(fml); } else { d = cls; @@ -733,7 +738,7 @@ public: m_correction_set_size = correction_set_size; } - TRACE("opt", tout << *mdl;); + TRACE("opt_verbose", tout << *mdl;); rational upper(0); @@ -756,10 +761,10 @@ public: m_model = mdl; m_c.model_updated(mdl.get()); - TRACE("opt", tout << "updated upper: " << upper << "\nmodel\n" << *m_model;); + TRACE("opt", tout << "updated upper: " << upper << "\n";); for (soft& s : m_soft) { - s.is_true = m_model->is_true(s.s); + s.set_value(m_model->is_true(s.s)); } verify_assignment(); @@ -833,22 +838,29 @@ public: void commit_assignment() override { if (m_found_feasible_optimum) { - TRACE("opt", tout << "Committing feasible solution\n" << m_defs << " " << m_asms;); add(m_defs); add(m_asms); + TRACE("opt", tout << "Committing feasible solution\ndefs:" << m_defs << "\nasms:" << m_asms << "\n";); + } // else: there is only a single assignment to these soft constraints. } void verify_core(exprs const& core) { return; - IF_VERBOSE(3, verbose_stream() << "verify core " << s().check_sat(core.size(), core.c_ptr()) << "\n";); + IF_VERBOSE(1, verbose_stream() << "verify core " << s().check_sat(core.size(), core.c_ptr()) << "\n";); ref _solver = mk_smt_solver(m, m_params, symbol()); _solver->assert_expr(s().get_assertions()); _solver->assert_expr(core); lbool is_sat = _solver->check_sat(0, nullptr); - IF_VERBOSE(0, verbose_stream() << "core status (l_false:) " << is_sat << "\n"); - VERIFY(is_sat == l_false); + IF_VERBOSE(0, verbose_stream() << "core status (l_false:) " << is_sat << " core size " << core.size() << "\n"); + CTRACE("opt", is_sat != l_false, + for (expr* c : core) tout << "core: " << mk_pp(c, m) << "\n"; + _solver->display(tout); + tout << "other solver\n"; + s().display(tout); + ); + VERIFY(is_sat == l_false || m.canceled()); } void verify_assumptions() { @@ -870,7 +882,7 @@ public: expr_ref n(m); for (soft& s : m_soft) { n = s.s; - if (!s.is_true) { + if (!s.is_true()) { n = mk_not(m, n); } _solver->assert_expr(n); diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index dc4451b6c..5441c4def 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -18,17 +18,18 @@ Notes: --*/ #include +#include "util/uint_set.h" +#include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/pb_decl_plugin.h" #include "opt/maxsmt.h" #include "opt/maxres.h" +#include "opt/maxlex.h" #include "opt/wmax.h" #include "opt/opt_params.hpp" -#include "ast/ast_pp.h" -#include "util/uint_set.h" #include "opt/opt_context.h" #include "smt/theory_wmaxsat.h" #include "smt/theory_pb.h" -#include "ast/ast_util.h" -#include "ast/pb_decl_plugin.h" namespace opt { @@ -61,7 +62,7 @@ namespace opt { rational k(0), cost(0); vector weights; for (soft const& s : m_soft) { - if (s.is_true) { + if (s.is_true()) { k += s.weight; } else { @@ -80,13 +81,13 @@ namespace opt { m_lower.reset(); m_upper.reset(); for (soft& s : m_soft) { - s.is_true = m.is_true(s.s); - if (!s.is_true) m_upper += s.weight; + s.set_value(m.is_true(s.s)); + if (!s.is_true()) m_upper += s.weight; } TRACE("opt", tout << "upper: " << m_upper << " assignments: "; - for (soft& s : m_soft) tout << (s.is_true?"T":"F"); + for (soft& s : m_soft) tout << (s.is_true()?"T":"F"); tout << "\n";); return true; } @@ -231,10 +232,14 @@ namespace opt { lbool maxsmt::operator()() { lbool is_sat = l_undef; m_msolver = nullptr; + opt_params optp(m_params); symbol const& maxsat_engine = m_c.maxsat_engine(); IF_VERBOSE(1, verbose_stream() << "(maxsmt)\n";); TRACE("opt_verbose", s().display(tout << "maxsmt\n") << "\n";); - if (m_soft_constraints.empty() || maxsat_engine == symbol("maxres") || maxsat_engine == symbol::null) { + if (optp.maxlex_enable() && is_maxlex(m_weights)) { + m_msolver = mk_maxlex(m_c, m_index, m_weights, m_soft_constraints); + } + else if (m_soft_constraints.empty() || maxsat_engine == symbol("maxres") || maxsat_engine == symbol::null) { m_msolver = mk_maxres(m_c, m_index, m_weights, m_soft_constraints); } else if (maxsat_engine == symbol("pd-maxres")) { diff --git a/src/opt/maxsmt.h b/src/opt/maxsmt.h index b61d876b3..796510ce2 100644 --- a/src/opt/maxsmt.h +++ b/src/opt/maxsmt.h @@ -59,10 +59,13 @@ namespace opt { struct soft { expr_ref s; rational weight; - bool is_true; - soft(expr_ref const& s, rational const& w, bool t): s(s), weight(w), is_true(t) {} - soft(soft const& other):s(other.s), weight(other.weight), is_true(other.is_true) {} - soft& operator=(soft const& other) { s = other.s; weight = other.weight; is_true = other.is_true; return *this; } + lbool value; + void set_value(bool t) { value = t?l_true:l_undef; } + void set_value(lbool t) { value = t; } + bool is_true() const { return value == l_true; } + soft(expr_ref const& s, rational const& w, bool t): s(s), weight(w), value(t?l_true:l_undef) {} + soft(soft const& other):s(other.s), weight(other.weight), value(other.value) {} + soft& operator=(soft const& other) { s = other.s; weight = other.weight; value = other.value; return *this; } }; ast_manager& m; maxsat_context& m_c; @@ -84,7 +87,7 @@ namespace opt { ~maxsmt_solver_base() override {} rational get_lower() const override { return m_lower; } rational get_upper() const override { return m_upper; } - bool get_assignment(unsigned index) const override { return m_soft[index].is_true; } + bool get_assignment(unsigned index) const override { return m_soft[index].is_true(); } void collect_statistics(statistics& st) const override { } void get_model(model_ref& mdl, svector& labels) override { mdl = m_model.get(); labels = m_labels;} virtual void commit_assignment(); diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index a6f3d8152..d57cee2a4 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -48,6 +48,7 @@ Notes: namespace opt { void context::scoped_state::push() { + m_asms_lim.push_back(m_asms.size()); m_hard_lim.push_back(m_hard.size()); m_objectives_lim.push_back(m_objectives.size()); m_objectives_term_trail_lim.push_back(m_objectives_term_trail.size()); @@ -55,6 +56,7 @@ namespace opt { void context::scoped_state::pop() { m_hard.resize(m_hard_lim.back()); + m_asms.resize(m_asms_lim.back()); unsigned k = m_objectives_term_trail_lim.back(); while (m_objectives_term_trail.size() > k) { unsigned idx = m_objectives_term_trail.back(); @@ -73,6 +75,7 @@ namespace opt { } m_objectives_lim.pop_back(); m_hard_lim.pop_back(); + m_asms_lim.pop_back(); } void context::scoped_state::add(expr* hard) { @@ -125,7 +128,7 @@ namespace opt { m_solver(nullptr), m_pareto1(false), m_box_index(UINT_MAX), - m_optsmt(m), + m_optsmt(m, *this), m_scoped_state(m), m_fm(alloc(generic_model_converter, m, "opt")), m_model_fixed(), @@ -188,6 +191,12 @@ namespace opt { clear_state(); } + void context::add_hard_constraint(expr* f, expr* t) { + m_scoped_state.m_asms.push_back(t); + m_scoped_state.add(m.mk_implies(t, f)); + clear_state(); + } + void context::get_hard_constraints(expr_ref_vector& hard) { hard.append(m_scoped_state.m_hard); } @@ -253,7 +262,7 @@ namespace opt { m_hard_constraints.append(s.m_hard); } - lbool context::optimize(expr_ref_vector const& asms) { + lbool context::optimize(expr_ref_vector const& _asms) { if (m_pareto) { return execute_pareto(); } @@ -263,6 +272,8 @@ namespace opt { clear_state(); init_solver(); import_scoped_state(); + expr_ref_vector asms(_asms); + asms.append(m_scoped_state.m_asms); normalize(asms); if (m_hard_constraints.size() == 1 && m.is_false(m_hard_constraints.get(0))) { return l_false; @@ -300,6 +311,7 @@ namespace opt { } return is_sat; } + s.assert_expr(asms); IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n"); TRACE("opt", model_smt2_pp(tout, m, *m_model, 0);); m_optsmt.setup(*m_opt_solver.get()); @@ -356,11 +368,22 @@ namespace opt { } } + void context::set_model(model_ref& m) { + m_model = m; + opt_params optp(m_params); + if (optp.dump_models()) { + model_ref md = m->copy(); + fix_model(md); + std::cout << *md << "\n"; + } + } + + void context::get_model_core(model_ref& mdl) { mdl = m_model; fix_model(mdl); if (mdl) mdl->set_model_completion(true); - TRACE("opt", tout << *mdl;); + CTRACE("opt", mdl, tout << *mdl;); } void context::get_box_model(model_ref& mdl, unsigned index) { @@ -382,7 +405,7 @@ namespace opt { lbool context::execute_min_max(unsigned index, bool committed, bool scoped, bool is_max) { if (scoped) get_solver().push(); lbool result = m_optsmt.lex(index, is_max); - if (result == l_true) m_optsmt.get_model(m_model, m_labels); + if (result == l_true) { m_optsmt.get_model(m_model, m_labels); SASSERT(m_model); } if (scoped) get_solver().pop(1); if (result == l_true && committed) m_optsmt.commit_assignment(index); if (result == l_true && m_optsmt.is_unbounded(index, is_max) && contains_quantifiers()) { @@ -436,7 +459,7 @@ namespace opt { for (unsigned i = 0; r == l_true && i < sz; ++i) { objective const& o = m_objectives[i]; bool is_last = i + 1 == sz; - r = execute(o, i + 1 < sz, sc && !is_last && o.m_type != O_MAXSMT); + r = execute(o, i + 1 < sz, sc && !is_last); if (r == l_true && o.m_type == O_MINIMIZE && !get_lower_as_num(i).is_finite()) { return r; } @@ -481,7 +504,7 @@ namespace opt { ++j; } } - if (r == l_true && m_box_models.size() > 0) { + if (r == l_true && !m_box_models.empty()) { m_model = m_box_models[0]; } return r; @@ -853,7 +876,10 @@ namespace opt { bool& neg, symbol& id, expr_ref& orig_term, unsigned& index) { if (!is_app(fml)) return false; neg = false; + orig_term = nullptr; + index = 0; app* a = to_app(fml); + if (m_objective_fns.find(a->get_decl(), index) && m_objectives[index].m_type == O_MAXSMT) { for (unsigned i = 0; i < a->get_num_args(); ++i) { expr_ref arg(a->get_arg(i), m); @@ -995,13 +1021,13 @@ namespace opt { void context::from_fmls(expr_ref_vector const& fmls) { TRACE("opt", tout << fmls << "\n";); m_hard_constraints.reset(); - expr_ref orig_term(m); for (expr * fml : fmls) { app_ref tr(m); + expr_ref orig_term(m); expr_ref_vector terms(m); vector weights; rational offset(0); - unsigned index; + unsigned index = 0; symbol id; bool neg; if (is_maxsat(fml, terms, weights, offset, neg, id, orig_term, index)) { @@ -1058,6 +1084,9 @@ namespace opt { void context::model_updated(model* md) { + model_ref mdl = md; + set_model(mdl); +#if 0 opt_params optp(m_params); symbol prefix = optp.solution_prefix(); if (prefix == symbol::null || prefix == symbol("")) return; @@ -1070,6 +1099,7 @@ namespace opt { out << *mdl; out.close(); } +#endif } @@ -1573,7 +1603,6 @@ namespace opt { m_model_converter->display(verbose_stream() << "mc\n"); model_smt2_pp(verbose_stream(), m, *mdl, 0); verbose_stream() << to_string_internal() << "\n"); - exit(0); } } } @@ -1596,7 +1625,7 @@ namespace opt { value = obj.m_adjust_value(value); rational value0 = ms.get_lower(); TRACE("opt", tout << "value " << value << " " << value0 << "\n";); - SASSERT(value == value0); + // TBD is this correct? SASSERT(value == value0); } } } @@ -1604,6 +1633,7 @@ namespace opt { void context::validate_lex() { rational r1; expr_ref val(m); + SASSERT(m_model); for (unsigned i = 0; i < m_objectives.size(); ++i) { objective const& obj = m_objectives[i]; switch(obj.m_type) { @@ -1632,7 +1662,10 @@ namespace opt { } // TBD: check that optimal was not changed. } - TRACE("opt", tout << "value " << value << "\n";); + maxsmt& ms = *m_maxsmts.find(obj.m_id); + rational value0 = ms.get_lower(); + TRACE("opt", tout << "value " << value << " other " << value0 << "\n";); + // TBD SASSERT(value0 == value); break; } } diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index fe0d4a13e..5959c4d19 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -115,20 +115,23 @@ namespace opt { arith_util m_arith; bv_util m_bv; unsigned_vector m_hard_lim; + unsigned_vector m_asms_lim; unsigned_vector m_objectives_lim; unsigned_vector m_objectives_term_trail; unsigned_vector m_objectives_term_trail_lim; map_id m_indices; public: - expr_ref_vector m_hard; + expr_ref_vector m_hard; + expr_ref_vector m_asms; vector m_objectives; scoped_state(ast_manager& m): m(m), m_arith(m), m_bv(m), - m_hard(m) + m_hard(m), + m_asms(m) {} void push(); void pop(); @@ -180,6 +183,7 @@ namespace opt { unsigned add_soft_constraint(expr* f, rational const& w, symbol const& id); unsigned add_objective(app* t, bool is_max); void add_hard_constraint(expr* f); + void add_hard_constraint(expr* f, expr* t); void get_hard_constraints(expr_ref_vector& hard); expr_ref get_objective(unsigned i); @@ -189,7 +193,7 @@ namespace opt { bool empty() override { return m_scoped_state.m_objectives.empty(); } void set_hard_constraints(expr_ref_vector const& hard) override; lbool optimize(expr_ref_vector const& asms) override; - void set_model(model_ref& _m) override { m_model = _m; } + void set_model(model_ref& _m) override; void get_model_core(model_ref& _m) override; void get_box_model(model_ref& _m, unsigned index) override; void fix_model(model_ref& _m) override; diff --git a/src/opt/opt_params.pyg b/src/opt/opt_params.pyg index f72bafbd8..dd9e26aae 100644 --- a/src/opt/opt_params.pyg +++ b/src/opt/opt_params.pyg @@ -5,6 +5,7 @@ def_module_params('opt', ('maxsat_engine', SYMBOL, 'maxres', "select engine for maxsat: 'core_maxsat', 'wmax', 'maxres', 'pd-maxres'"), ('priority', SYMBOL, 'lex', "select how to priortize objectives: 'lex' (lexicographic), 'pareto', 'box'"), ('dump_benchmarks', BOOL, False, 'dump benchmarks for profiling'), + ('dump_models', BOOL, False, 'display intermediary models to stdout'), ('solution_prefix', SYMBOL, '', "path prefix to dump intermediary, but non-optimal, solutions"), ('timeout', UINT, UINT_MAX, 'timeout (in milliseconds) (UINT_MAX and 0 mean no timeout)'), ('rlimit', UINT, 0, 'resource limit (0 means no limit)'), @@ -13,6 +14,7 @@ def_module_params('opt', ('elim_01', BOOL, True, 'eliminate 01 variables'), ('pp.neat', BOOL, True, 'use neat (as opposed to less readable, but faster) pretty printer when displaying context'), ('pb.compile_equality', BOOL, False, 'compile arithmetical equalities into pseudo-Boolean equality (instead of two inequalites)'), + ('maxlex.enable', BOOL, True, 'enable maxlex heuristic for lexicographic MaxSAT problems'), ('maxres.hill_climb', BOOL, True, 'give preference for large weight cores'), ('maxres.add_upper_bound_block', BOOL, False, 'restict upper bound with constraint'), ('maxres.max_num_cores', UINT, UINT_MAX, 'maximal number of cores per round'), diff --git a/src/opt/opt_solver.cpp b/src/opt/opt_solver.cpp index e61a02d80..80a81ba3a 100644 --- a/src/opt/opt_solver.cpp +++ b/src/opt/opt_solver.cpp @@ -158,7 +158,7 @@ namespace opt { return m_dump_benchmarks; } - lbool opt_solver::check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + lbool opt_solver::check_sat_core2(unsigned num_assumptions, expr * const * assumptions) { TRACE("opt_verbose", { tout << "context size: " << m_context.size() << "\n"; for (unsigned i = 0; i < m_context.size(); ++i) { @@ -208,6 +208,9 @@ namespace opt { return m_context.preferred_sat(asms, cores); } + void opt_solver::get_levels(ptr_vector const& vars, unsigned_vector& depth) { + return m_context.get_levels(vars, depth); + } /** diff --git a/src/opt/opt_solver.h b/src/opt/opt_solver.h index 39562ec54..be71376ac 100644 --- a/src/opt/opt_solver.h +++ b/src/opt/opt_solver.h @@ -95,7 +95,7 @@ namespace opt { void assert_expr_core(expr * t) override; void push_core() override; void pop_core(unsigned n) override; - lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override; + lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) override; void get_unsat_core(expr_ref_vector & r) override; void get_model_core(model_ref & _m) override; proof * get_proof() override; @@ -108,6 +108,9 @@ namespace opt { ast_manager& get_manager() const override { return m; } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override; lbool preferred_sat(expr_ref_vector const& asms, vector& cores) override; + void get_levels(ptr_vector const& vars, unsigned_vector& depth) override; + expr_ref_vector get_trail() override { return m_context.get_trail(); } + void set_activity(expr* lit, double act) override { m_context.set_activity(lit, act); } expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } void set_logic(symbol const& logic); diff --git a/src/opt/optsmt.cpp b/src/opt/optsmt.cpp index a461d4a22..4a35acd02 100644 --- a/src/opt/optsmt.cpp +++ b/src/opt/optsmt.cpp @@ -31,6 +31,7 @@ Notes: #include #include "opt/optsmt.h" #include "opt/opt_solver.h" +#include "opt/opt_context.h" #include "ast/arith_decl_plugin.h" #include "smt/theory_arith.h" #include "ast/ast_pp.h" @@ -171,6 +172,7 @@ namespace opt { } lbool optsmt::geometric_lex(unsigned obj_index, bool is_maximize) { + TRACE("opt", tout << "index: " << obj_index << " is-max: " << is_maximize << "\n";); arith_util arith(m); bool is_int = arith.is_int(m_objs[obj_index].get()); lbool is_sat = l_true; @@ -189,9 +191,11 @@ namespace opt { SASSERT(delta_per_step.is_int()); SASSERT(delta_per_step.is_pos()); is_sat = m_s->check_sat(0, nullptr); + TRACE("opt", tout << "check " << is_sat << "\n";); if (is_sat == l_true) { m_s->maximize_objective(obj_index, bound); m_s->get_model(m_model); + SASSERT(m_model); m_s->get_labels(m_labels); inf_eps obj = m_s->saved_objective_value(obj_index); update_lower_lex(obj_index, obj, is_maximize); @@ -220,13 +224,17 @@ namespace opt { delta_per_step = rational::one(); SASSERT(num_scopes > 0); --num_scopes; - m_s->pop(1); + m_s->pop(1); } else { break; } } m_s->pop(num_scopes); + + if (is_sat == l_false && !m_model) { + return l_false; + } if (m.canceled() || is_sat == l_undef) { return l_undef; @@ -355,12 +363,14 @@ namespace opt { verbose_stream() << "(optsmt lower bound: " << v << ")\n"; else verbose_stream() << "(optsmt upper bound: " << (-v) << ")\n"; - ); + ); expr_ref tmp(m); for (unsigned i = idx+1; i < m_vars.size(); ++i) { m_s->maximize_objective(i, tmp); m_lower[i] = m_s->saved_objective_value(i); } + + m_context.set_model(m_model); } } @@ -574,7 +584,7 @@ namespace opt { return m_upper[i]; } - void optsmt::get_model(model_ref& mdl, svector & labels) { + void optsmt::get_model(model_ref& mdl, svector & labels) { mdl = m_model.get(); labels = m_labels; } diff --git a/src/opt/optsmt.h b/src/opt/optsmt.h index 6db7eaadf..c9aa4f41d 100644 --- a/src/opt/optsmt.h +++ b/src/opt/optsmt.h @@ -27,8 +27,11 @@ namespace opt { Returns an optimal assignment to objective functions. */ + class context; + class optsmt { ast_manager& m; + context& m_context; opt_solver* m_s; vector m_lower; vector m_upper; @@ -40,8 +43,8 @@ namespace opt { svector m_labels; sref_vector m_models; public: - optsmt(ast_manager& m): - m(m), m_s(nullptr), m_objs(m), m_lower_fmls(m) {} + optsmt(ast_manager& m, context& ctx): + m(m), m_context(ctx), m_s(nullptr), m_objs(m), m_lower_fmls(m) {} void setup(opt_solver& solver); diff --git a/src/opt/pb_sls.cpp b/src/opt/pb_sls.cpp index 9054e2b00..17c3f9128 100644 --- a/src/opt/pb_sls.cpp +++ b/src/opt/pb_sls.cpp @@ -403,10 +403,8 @@ namespace smt { } int new_break_count = flip(~lit); if (-break_count != new_break_count) { - verbose_stream() << lit << "\n"; - IF_VERBOSE(0, display(verbose_stream(), cls);); - display(verbose_stream()); - exit(0); + IF_VERBOSE(0, display(verbose_stream() << lit << "\n", cls); + display(verbose_stream())); } // VERIFY(-break_count == flip(~lit)); } diff --git a/src/opt/sortmax.cpp b/src/opt/sortmax.cpp index df361f24a..1a19032e2 100644 --- a/src/opt/sortmax.cpp +++ b/src/opt/sortmax.cpp @@ -114,7 +114,7 @@ namespace opt { } void update_assignment() { - for (soft& s : m_soft) s.is_true = is_true(s.s); + for (soft& s : m_soft) s.set_value(is_true(s.s)); } bool is_true(expr* e) { diff --git a/src/opt/wmax.cpp b/src/opt/wmax.cpp index ee7f92a23..9fb683179 100644 --- a/src/opt/wmax.cpp +++ b/src/opt/wmax.cpp @@ -75,11 +75,9 @@ namespace opt { trace_bounds("wmax"); TRACE("opt", - s().display(tout); tout << "\n"; + s().display(tout)<< "\n"; tout << "lower: " << m_lower << " upper: " << m_upper << "\n";); while (!m.canceled() && m_lower < m_upper) { - //mk_assumptions(asms); - //is_sat = s().preferred_sat(asms, cores); is_sat = s().check_sat(0, nullptr); if (m.canceled()) { is_sat = l_undef; @@ -124,7 +122,7 @@ namespace opt { } void update_assignment() { - for (soft& s : m_soft) s.is_true = is_true(s.s); + for (soft& s : m_soft) s.set_value(is_true(s.s)); } struct compare_asm { diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 64f9c36fd..bf1e25e57 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -1371,11 +1371,11 @@ namespace smt2 { else { while (!curr_is_rparen()) { m_env.begin_scope(); + check_lparen_next("invalid pattern binding, '(' expected"); unsigned num_bindings = m_num_bindings; parse_match_pattern(srt); patterns.push_back(expr_stack().back()); expr_stack().pop_back(); - check_lparen_next("invalid pattern binding, '(' expected"); parse_expr(); cases.push_back(expr_stack().back()); expr_stack().pop_back(); @@ -1474,22 +1474,29 @@ namespace smt2 { * _ * x */ - - bool parse_constructor_pattern(sort * srt) { - if (!curr_is_lparen()) { - return false; - } - next(); + + void parse_match_pattern(sort * srt) { + symbol C; svector vars; expr_ref_vector args(m()); - symbol C(check_identifier_next("constructor symbol expected")); - while (!curr_is_rparen()) { - symbol v(check_identifier_next("variable symbol expected")); - if (v != m_underscore && vars.contains(v)) { - throw parser_exception("unexpected repeated variable in pattern expression"); - } - vars.push_back(v); - } + + if (curr_is_identifier()) { + C = curr_id(); + } + else if (curr_is_lparen()) { + next(); + C = check_identifier_next("constructor symbol expected"); + while (!curr_is_rparen()) { + symbol v(check_identifier_next("variable symbol expected")); + if (v != m_underscore && vars.contains(v)) { + throw parser_exception("unexpected repeated variable in pattern expression"); + } + vars.push_back(v); + } + } + else { + throw parser_exception("expecting a constructor, _, variable or constructor application"); + } next(); // now have C, vars @@ -1498,10 +1505,28 @@ namespace smt2 { // store expression in expr_stack(). // ensure that bound variables are adjusted to vars - func_decl* f = m_ctx.find_func_decl(C, 0, nullptr, vars.size(), nullptr, srt); - if (!f) { + func_decl* f = nullptr; + try { + f = m_ctx.find_func_decl(C, 0, nullptr, vars.size(), nullptr, srt); + } + catch (cmd_exception &) { + if (!args.empty()) { + throw; + } + } + + if (!f && !args.empty()) { throw parser_exception("expecting a constructor that has been declared"); } + if (!f) { + m_num_bindings++; + var * v = m().mk_var(0, srt); + if (C != m_underscore) { + m_env.insert(C, local(v, m_num_bindings)); + } + expr_stack().push_back(v); + return; + } if (!dtutil().is_constructor(f)) { throw parser_exception("expecting a constructor"); } @@ -1517,40 +1542,6 @@ namespace smt2 { } } expr_stack().push_back(m().mk_app(f, args.size(), args.c_ptr())); - return true; - } - - void parse_match_pattern(sort* srt) { - if (parse_constructor_pattern(srt)) { - // done - } - else if (curr_id() == m_underscore) { - // we have a wild-card. - // store dummy variable in expr_stack() - next(); - var* v = m().mk_var(0, srt); - expr_stack().push_back(v); - } - else { - symbol xC(check_identifier_next("constructor symbol or variable expected")); - // check if xC is a constructor, otherwise make it a variable - // of sort srt. - try { - func_decl* f = m_ctx.find_func_decl(xC, 0, nullptr, 0, nullptr, srt); - if (!dtutil().is_constructor(f)) { - throw parser_exception("expecting a constructor, got a previously declared function"); - } - if (f->get_arity() > 0) { - throw parser_exception("constructor expects arguments, but no arguments were supplied in pattern"); - } - expr_stack().push_back(m().mk_const(f)); - } - catch (cmd_exception &) { - var* v = m().mk_var(0, srt); - expr_stack().push_back(v); - m_env.insert(xC, local(v, m_num_bindings++)); - } - } } symbol parse_indexed_identifier_core() { diff --git a/src/qe/nlarith_util.cpp b/src/qe/nlarith_util.cpp index 12977b449..c2d95a1ee 100644 --- a/src/qe/nlarith_util.cpp +++ b/src/qe/nlarith_util.cpp @@ -684,7 +684,7 @@ namespace nlarith { void get_coefficients(poly const& p, app*& a, app*& b, app*& c) { a = b = c = z(); - if (p.size() > 0) c = p[0]; + if (!p.empty()) c = p[0]; if (p.size() > 1) b = p[1]; if (p.size() > 2) a = p[2]; SASSERT(p.size() <= 3); @@ -1359,7 +1359,7 @@ namespace nlarith { void quot_rem(poly const& u, poly const& v, poly& q, poly& r, app_ref& lc, unsigned& power) { lc = v.empty()?num(0):v[v.size()-1]; power = 0; - if (u.size() < v.size() || v.size() == 0) { + if (u.size() < v.size() || v.empty()) { q.reset(); r.reset(); r.append(u); diff --git a/src/qe/qe.h b/src/qe/qe.h index 1027f0b61..1eb7b7e4a 100644 --- a/src/qe/qe.h +++ b/src/qe/qe.h @@ -253,7 +253,7 @@ namespace qe { /** \brief Guarded definitions. - A realizer to a an existential quantified formula is a disjunction + A realizer to an existential quantified formula is a disjunction together with a substitution from the existentially quantified variables to terms such that: 1. The original formula (exists (vars) fml) is equivalent to the disjunction of guards. diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index cd81167e3..a0aea08da 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -387,7 +387,7 @@ namespace qe { for (row const& r : rows) { expr_ref_vector ts(m); expr_ref t(m), s(m), val(m); - if (r.m_vars.size() == 0) { + if (r.m_vars.empty()) { continue; } if (r.m_vars.size() == 1 && r.m_vars[0].m_coeff.is_neg() && r.m_type != opt::t_mod) { diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index 4c81418b6..4bead6684 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -798,7 +798,7 @@ namespace qe { */ expr* reduce_core (app *a) { if (!m_arr_u.is_store (a->get_arg (0))) return a; - expr* array = a->get_arg(0); + expr* array = a->get_arg(0); unsigned arity = get_array_arity(m.get_sort(array)); expr* const* js = a->get_args() + 1; @@ -810,7 +810,7 @@ namespace qe { if (is_equals (arity, idxs, js)) { add_idx_cond (cond); - return a->get_arg (2); + return a->get_arg (a->get_num_args() - 1); } else { cond = m.mk_not (cond); diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index 3226f7554..4585c88c1 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -2370,6 +2370,7 @@ void qe_lite::operator()(uint_set const& index_set, bool index_of_bound, expr_re (*m_impl)(index_set, index_of_bound, fmls); } +namespace { class qe_lite_tactic : public tactic { struct imp { @@ -2494,7 +2495,6 @@ public: (*m_imp)(in, result); } - void collect_statistics(statistics & st) const override { // m_imp->collect_statistics(st); } @@ -2503,14 +2503,14 @@ public: // m_imp->reset_statistics(); } - void cleanup() override { ast_manager & m = m_imp->m; - dealloc(m_imp); - m_imp = alloc(imp, m, m_params); + m_imp->~imp(); + m_imp = new (m_imp) imp(m, m_params); } }; +} tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p) { return alloc(qe_lite_tactic, m, p); diff --git a/src/qe/qe_lite.h b/src/qe/qe_lite.h index 63ad8bedd..3251fa3f8 100644 --- a/src/qe/qe_lite.h +++ b/src/qe/qe_lite.h @@ -18,8 +18,7 @@ Revision History: --*/ -#ifndef QE_LITE_H_ -#define QE_LITE_H_ +#pragma once #include "ast/ast.h" #include "util/uint_set.h" @@ -67,5 +66,3 @@ tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p = params_ref()) /* ADD_TACTIC("qe-light", "apply light-weight quantifier elimination.", "mk_qe_lite_tactic(m, p)") */ - -#endif diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index b7f0d0d49..77f910260 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -30,6 +30,7 @@ Notes: #include "ast/ast_util.h" #include "ast/for_each_expr.h" +#include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/arith_decl_plugin.h" #include "model/model_evaluator.h" @@ -95,101 +96,6 @@ namespace qe { m_solver->assert_expr(mk_not(mk_and(lits))); } - // ------------------------------- - // euf_mbi - - struct euf_mbi_plugin::is_atom_proc { - ast_manager& m; - expr_ref_vector& m_atoms; - is_atom_proc(expr_ref_vector& atoms): m(atoms.m()), m_atoms(atoms) {} - void operator()(app* a) { - if (m.is_eq(a)) { - m_atoms.push_back(a); - } - else if (m.is_bool(a) && a->get_family_id() != m.get_basic_family_id()) { - m_atoms.push_back(a); - } - } - void operator()(expr*) {} - }; - - euf_mbi_plugin::euf_mbi_plugin(solver* s, solver* sNot): - mbi_plugin(s->get_manager()), - m_atoms(m), - m_solver(s), - m_dual_solver(sNot) { - params_ref p; - p.set_bool("core.minimize", true); - m_solver->updt_params(p); - m_dual_solver->updt_params(p); - expr_ref_vector fmls(m); - m_solver->get_assertions(fmls); - expr_fast_mark1 marks; - is_atom_proc proc(m_atoms); - for (expr* e : fmls) { - quick_for_each_expr(proc, marks, e); - } - } - - mbi_result euf_mbi_plugin::operator()(expr_ref_vector& lits, model_ref& mdl) { - lbool r = m_solver->check_sat(lits); - switch (r) { - case l_false: - lits.reset(); - m_solver->get_unsat_core(lits); - // optionally minimize core using superposition. - return mbi_unsat; - case l_true: { - m_solver->get_model(mdl); - model_evaluator mev(*mdl.get()); - lits.reset(); - for (expr* e : m_atoms) { - if (mev.is_true(e)) { - lits.push_back(e); - } - else if (mev.is_false(e)) { - lits.push_back(m.mk_not(e)); - } - } - TRACE("qe", tout << "atoms from model: " << lits << "\n";); - r = m_dual_solver->check_sat(lits); - expr_ref_vector core(m); - term_graph tg(m); - switch (r) { - case l_false: - // use the dual solver to find a 'small' implicant - m_dual_solver->get_unsat_core(core); - TRACE("qe", tout << "core: " << core << "\n";); - // project the implicant onto vars - tg.set_vars(m_shared, false); - tg.add_lits(core); - lits.reset(); - lits.append(tg.project(*mdl)); - TRACE("qe", tout << "project: " << lits << "\n";); - return mbi_sat; - case l_undef: - return mbi_undef; - case l_true: - UNREACHABLE(); - return mbi_undef; - } - return mbi_sat; - } - default: - // TBD: if not running solver to completion, then: - // 1. extract unit literals from m_solver. - // 2. run a cc over the units - // 3. extract equalities or other assignments over the congruence classes - // 4. ensure that at least some progress is made over original lits. - return mbi_undef; - } - } - - void euf_mbi_plugin::block(expr_ref_vector const& lits) { - m_solver->assert_expr(mk_not(mk_and(lits))); - } - - // ------------------------------- // euf_arith_mbi @@ -219,29 +125,49 @@ namespace qe { struct euf_arith_mbi_plugin::is_arith_var_proc { ast_manager& m; - app_ref_vector& m_pvars; - app_ref_vector& m_svars; - arith_util arith; - obj_hashtable m_shared; - is_arith_var_proc(func_decl_ref_vector const& shared, app_ref_vector& pvars, app_ref_vector& svars): - m(pvars.m()), m_pvars(pvars), m_svars(svars), arith(m) { - for (func_decl* f : shared) m_shared.insert(f); + app_ref_vector& m_avars; + app_ref_vector& m_proxies; + arith_util m_arith; + obj_hashtable m_seen; + is_arith_var_proc(app_ref_vector& avars, app_ref_vector& proxies): + m(avars.m()), m_avars(avars), m_proxies(proxies), m_arith(m) { } void operator()(app* a) { - if (!arith.is_int_real(a) || a->get_family_id() == arith.get_family_id()) { - // no-op + if (is_arith_op(a) || a->get_family_id() == m.get_basic_family_id()) { + return; } - else if (m_shared.contains(a->get_decl())) { - m_svars.push_back(a); + + if (m_arith.is_int_real(a)) { + m_avars.push_back(a); } - else { - m_pvars.push_back(a); + for (expr* arg : *a) { + if (is_app(arg) && !m_seen.contains(arg) && m_arith.is_int_real(arg)) { + m_proxies.push_back(to_app(arg)); + m_seen.insert(arg); + } } } + bool is_arith_op(app* a) { + return a->get_family_id() == m_arith.get_family_id(); + } void operator()(expr*) {} - }; + void euf_arith_mbi_plugin::filter_private_arith(app_ref_vector& avars) { + arith_util a(m); + unsigned j = 0; + obj_hashtable shared; + for (func_decl* f : m_shared) shared.insert(f); + for (unsigned i = 0; i < avars.size(); ++i) { + app* v = avars.get(i); + if (!shared.contains(v->get_decl()) && + v->get_family_id() != a.get_family_id()) { + avars[j++] = v; + } + } + avars.shrink(j); + } + euf_arith_mbi_plugin::euf_arith_mbi_plugin(solver* s, solver* sNot): mbi_plugin(s->get_manager()), m_atoms(m), @@ -290,28 +216,15 @@ namespace qe { } } - app_ref_vector euf_arith_mbi_plugin::get_arith_vars(model_ref& mdl, expr_ref_vector& lits) { - arith_util a(m); - app_ref_vector pvars(m), svars(m); // private and shared arithmetic variables. - is_arith_var_proc _proc(m_shared, pvars, svars); + + /** + * \brief extract arithmetical variables and arithmetical terms in shared positions. + */ + app_ref_vector euf_arith_mbi_plugin::get_arith_vars(model_ref& mdl, expr_ref_vector& lits, app_ref_vector& proxies) { + app_ref_vector avars(m); + is_arith_var_proc _proc(avars, proxies); for_each_expr(_proc, lits); - rational v1, v2; - for (expr* p : pvars) { - VERIFY(a.is_numeral((*mdl)(p), v1)); - for (expr* s : svars) { - VERIFY(a.is_numeral((*mdl)(s), v2)); - if (v1 < v2) { - lits.push_back(a.mk_lt(p, s)); - } - else if (v2 < v1) { - lits.push_back(a.mk_lt(s, p)); - } - else { - lits.push_back(m.mk_eq(s, p)); - } - } - } - return pvars; + return avars; } mbi_result euf_arith_mbi_plugin::operator()(expr_ref_vector& lits, model_ref& mdl) { @@ -324,93 +237,13 @@ namespace qe { TRACE("qe", tout << "unsat core: " << lits << "\n";); // optionally minimize core using superposition. return mbi_unsat; - case l_true: { + case l_true: m_solver->get_model(mdl); if (!get_literals(mdl, lits)) { return mbi_undef; } - TRACE("qe", tout << lits << "\n";); - - // 1. Extract projected variables, add inequalities between - // projected variables and non-projected terms according to model. - // 2. Extract disequalities implied by congruence closure. - // 3. project arithmetic variables from pure literals. - // 4. Add projected definitions as equalities to EUF. - // 5. project remaining literals with respect to EUF. - - app_ref_vector avars = get_arith_vars(mdl, lits); - TRACE("qe", tout << "vars: " << avars << " lits: " << lits << "\n";); - - // 2. - term_graph tg1(m); - func_decl_ref_vector no_shared(m); - tg1.set_vars(no_shared, false); - tg1.add_lits(lits); - arith_util a(m); - expr_ref_vector foreign = tg1.shared_occurrences(a.get_family_id()); - obj_hashtable _foreign; - for (expr* e : foreign) _foreign.insert(e); - vector partition = tg1.get_partition(*mdl); - expr_ref_vector diseq = tg1.get_ackerman_disequalities(); - lits.append(diseq); - TRACE("qe", tout << "diseq: " << diseq << "\n"; - tout << "foreign: " << foreign << "\n"; - for (auto const& v : partition) { - tout << "partition: {"; - bool first = true; - for (expr* e : v) { - if (first) first = false; else tout << ", "; - tout << expr_ref(e, m); - } - tout << "}\n"; - } - ); - vector refined_partition; - for (auto & p : partition) { - unsigned j = 0; - for (expr* e : p) { - if (_foreign.contains(e) || - (is_app(e) && m_shared.contains(to_app(e)->get_decl()))) { - p[j++] = e; - } - } - p.shrink(j); - if (!p.empty()) refined_partition.push_back(p); - } - TRACE("qe", - for (auto const& v : refined_partition) { - tout << "partition: {"; - bool first = true; - for (expr* e : v) { - if (first) first = false; else tout << ", "; - tout << expr_ref(e, m); - } - tout << "}\n"; - }); - - - - arith_project_plugin ap(m); - ap.set_check_purified(false); - - // 3. - auto defs = ap.project(*mdl.get(), avars, lits); - - // 4. - for (auto const& def : defs) { - lits.push_back(m.mk_eq(def.var, def.term)); - } - TRACE("qe", tout << "# arith defs " << defs.size() << " avars: " << avars << " " << lits << "\n";); - - // 5. - term_graph tg2(m); - tg2.set_vars(m_shared, false); - tg2.add_lits(lits); - lits.reset(); - lits.append(tg2.project()); - TRACE("qe", tout << "project: " << lits << "\n";); + project(mdl, lits); return mbi_sat; - } default: // TBD: if not running solver to completion, then: // 1. extract unit literals from m_solver. @@ -421,6 +254,106 @@ namespace qe { } } + void euf_arith_mbi_plugin::project(model_ref& mdl, expr_ref_vector& lits) { + TRACE("qe", tout << lits << "\n" << *mdl << "\n";); + TRACE("qe", tout << m_solver->get_assertions() << "\n";); + + + // 1. arithmetical variables - atomic and in purified positions + app_ref_vector proxies(m); + app_ref_vector avars = get_arith_vars(mdl, lits, proxies); + TRACE("qe", tout << "vars: " << avars << "\nproxies: " << proxies << "\nlits: " << lits << "\n";); + + // 2. project private non-arithmetical variables from lits + project_euf(mdl, lits, avars); + + // 3. Order arithmetical variables and purified positions + order_avars(mdl, lits, avars, proxies); + TRACE("qe", tout << "ordered: " << lits << "\n";); + + // 4. Perform arithmetical projection + arith_project_plugin ap(m); + ap.set_check_purified(false); + auto defs = ap.project(*mdl.get(), avars, lits); + TRACE("qe", tout << "aproject: " << lits << "\n";); + + // 5. Substitute solution into lits + substitute(defs, lits); + TRACE("qe", tout << "substitute: " << lits << "\n";); + } + + /** + * \brief substitute solution to arithmetical variables into lits + */ + void euf_arith_mbi_plugin::substitute(vector const& defs, expr_ref_vector& lits) { + for (auto const& def : defs) { + expr_safe_replace rep(m); + rep.insert(def.var, def.term); + rep(lits); + } + } + + /** + * \brief project non-arithmetical private symbols. + */ + void euf_arith_mbi_plugin::project_euf(model_ref& mdl, expr_ref_vector& lits, app_ref_vector& avars) { + term_graph tg(m); + func_decl_ref_vector shared(m_shared); + for (app* a : avars) shared.push_back(a->get_decl()); + tg.set_vars(shared, false); + tg.add_lits(lits); + lits.reset(); + lits.append(tg.project(*mdl.get())); + TRACE("qe", tout << "project: " << lits << "\n";); + } + + /** + * \brief Order arithmetical variables: + * 1. add literals that order the proxies according to the model. + * 2. sort arithmetical terms, such that deepest terms are first. + */ + void euf_arith_mbi_plugin::order_avars(model_ref& mdl, expr_ref_vector& lits, app_ref_vector& avars, app_ref_vector const& proxies) { + arith_util a(m); + model_evaluator mev(*mdl.get()); + vector> vals; + for (app* v : proxies) { + rational val; + expr_ref tmp = mev(v); + VERIFY(a.is_numeral(tmp, val)); + vals.push_back(std::make_pair(val, v)); + } + struct compare_first { + bool operator()(std::pair const& x, + std::pair const& y) const { + return x.first < y.first; + } + }; + // add linear order between proxies + compare_first cmp; + std::sort(vals.begin(), vals.end(), cmp); + for (unsigned i = 1; i < vals.size(); ++i) { + if (vals[i-1].first == vals[i].first) { + lits.push_back(m.mk_eq(vals[i-1].second, vals[i].second)); + } + else { + lits.push_back(a.mk_lt(vals[i-1].second, vals[i].second)); + } + } + + // filter out only private variables + filter_private_arith(avars); + + // sort avars based on depth + struct compare_depth { + bool operator()(app* x, app* y) const { + return x->get_depth() > y->get_depth(); + } + }; + compare_depth cmpd; + std::sort(avars.c_ptr(), avars.c_ptr() + avars.size(), cmpd); + TRACE("qe", tout << lits << "\navars:" << avars << "\n" << *mdl << "\n";); + } + void euf_arith_mbi_plugin::block(expr_ref_vector const& lits) { collect_atoms(lits); m_fmls.push_back(mk_not(mk_and(lits))); diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h index 1cc2be0cb..12e6a8080 100644 --- a/src/qe/qe_mbi.h +++ b/src/qe/qe_mbi.h @@ -20,6 +20,8 @@ Revision History: #pragma once +#include "qe/qe_arith.h" + namespace qe { enum mbi_result { mbi_sat, @@ -91,17 +93,6 @@ namespace qe { void block(expr_ref_vector const& lits) override; }; - class euf_mbi_plugin : public mbi_plugin { - expr_ref_vector m_atoms; - solver_ref m_solver; - solver_ref m_dual_solver; - struct is_atom_proc; - public: - euf_mbi_plugin(solver* s, solver* sNot); - ~euf_mbi_plugin() override {} - mbi_result operator()(expr_ref_vector& lits, model_ref& mdl) override; - void block(expr_ref_vector const& lits) override; - }; class euf_arith_mbi_plugin : public mbi_plugin { expr_ref_vector m_atoms; @@ -112,13 +103,18 @@ namespace qe { struct is_atom_proc; struct is_arith_var_proc; - app_ref_vector get_arith_vars(model_ref& mdl, expr_ref_vector& lits); + app_ref_vector get_arith_vars(model_ref& mdl, expr_ref_vector& lits, app_ref_vector& proxies); bool get_literals(model_ref& mdl, expr_ref_vector& lits); void collect_atoms(expr_ref_vector const& fmls); + void project_euf(model_ref& mdl, expr_ref_vector& lits, app_ref_vector& avars); + void order_avars(model_ref& mdl, expr_ref_vector& lits, app_ref_vector& avars, app_ref_vector const& proxies); + void substitute(vector const& defs, expr_ref_vector& lits); + void filter_private_arith(app_ref_vector& avars); public: euf_arith_mbi_plugin(solver* s, solver* emptySolver); ~euf_arith_mbi_plugin() override {} mbi_result operator()(expr_ref_vector& lits, model_ref& mdl) override; + void project(model_ref& mdl, expr_ref_vector& lits); void block(expr_ref_vector const& lits) override; }; diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index a0e16727e..becaeb049 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -501,7 +501,7 @@ public: expr_ref val(m); model_evaluator eval(model); for (expr * f : fmls) { - VERIFY(model.is_true(f)); + VERIFY(!model.is_false(f)); } return true; } @@ -657,7 +657,7 @@ public: other_vars.reset(); } - SASSERT(eval.is_true(fml)); + SASSERT(!eval.is_false(fml)); vars.reset (); vars.append(other_vars); diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index b5de20368..274c25293 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -774,11 +774,13 @@ namespace qe { TRACE("qe", tout << "literals: " << res << "\n";); } - void mk_distinct(expr_ref_vector& res) { - vector> decl2terms; // terms that use function f - ptr_vector decls; - decl2terms.reset(); + vector> m_decl2terms; // terms that use function f + ptr_vector m_decls; + + void collect_decl2terms() { // Collect the projected function symbols. + m_decl2terms.reset(); + m_decls.reset(); for (term *t : m_tg.m_terms) { expr* e = t->get_expr(); if (!is_app(e)) continue; @@ -787,38 +789,61 @@ namespace qe { func_decl* d = a->get_decl(); if (d->get_arity() == 0) continue; unsigned id = d->get_decl_id(); - decl2terms.reserve(id+1); - if (decl2terms[id].empty()) decls.push_back(d); - decl2terms[id].push_back(t); + m_decl2terms.reserve(id+1); + if (m_decl2terms[id].empty()) m_decls.push_back(d); + m_decl2terms[id].push_back(t); } - + } + + void args_are_distinct(expr_ref_vector& res) { // // for each projected function that occurs // (may occur) in multiple congruence classes, // produce assertions that non-congruent arguments - // are forced distinct. + // are distinct. // - for (func_decl* d : decls) { + for (func_decl* d : m_decls) { unsigned id = d->get_decl_id(); - ptr_vector const& terms = decl2terms[id]; + ptr_vector const& terms = m_decl2terms[id]; if (terms.size() <= 1) continue; unsigned arity = d->get_arity(); for (unsigned i = 0; i < arity; ++i) { - obj_hashtable roots; + obj_hashtable roots, root_vals; + expr_ref_vector pinned(m); for (term* t : terms) { expr* arg = to_app(t->get_expr())->get_arg(i); term const& root = m_tg.get_term(arg)->get_root(); - roots.insert(root.get_expr()); + expr* r = root.get_expr(); + // if a model is given, then use the equivalence class induced + // by the model. Otherwise, use the congruence class. + if (m_model) { + expr_ref tmp(m); + tmp = (*m_model)(r); + if (!root_vals.contains(tmp)) { + root_vals.insert(tmp); + roots.insert(r); + pinned.push_back(tmp); + } + } + else { + roots.insert(r); + } } if (roots.size() > 1) { ptr_buffer args; for (expr* r : roots) { args.push_back(r); } + TRACE("qe", tout << "function: " << d->get_name() << "\n";); res.push_back(m.mk_distinct(args.size(), args.c_ptr())); } } } + } + + void mk_distinct(expr_ref_vector& res) { + collect_decl2terms(); + args_are_distinct(res); TRACE("qe", tout << res << "\n";); } @@ -965,6 +990,7 @@ namespace qe { m_pinned.reset(); m_model.reset(); } + expr_ref_vector project() { expr_ref_vector res(m); purify(); diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index 2ad5b9b96..d1816d807 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -549,9 +549,15 @@ namespace qe { solver& s() { return *m_solver; } solver const& s() const { return *m_solver; } - void reset() { + void init() { m_solver = mk_smt_solver(m, m_params, symbol::null); } + void reset_statistics() { + init(); + } + void clear() { + m_solver = nullptr; + } void assert_expr(expr* e) { m_solver->assert_expr(e); } @@ -696,7 +702,7 @@ namespace qe { m_level -= num_scopes; } - void reset() override { + void clear() { m_st.reset(); m_fa.s().collect_statistics(m_st); m_ex.s().collect_statistics(m_st); @@ -707,9 +713,15 @@ namespace qe { m_pred_abs.reset(); m_vars.reset(); m_model = nullptr; - m_fa.reset(); - m_ex.reset(); m_free_vars.reset(); + m_fa.clear(); + m_ex.clear(); + } + + void reset() override { + clear(); + m_fa.init(); + m_ex.init(); } /** @@ -1198,7 +1210,7 @@ namespace qe { } ~qsat() override { - reset(); + clear(); } void updt_params(params_ref const & p) override { @@ -1294,8 +1306,8 @@ namespace qe { void reset_statistics() override { m_stats.reset(); - m_fa.reset(); - m_ex.reset(); + m_fa.reset_statistics(); + m_ex.reset_statistics(); } void cleanup() override { diff --git a/src/sat/ba_solver.cpp b/src/sat/ba_solver.cpp index 762939b74..d97e3ee94 100644 --- a/src/sat/ba_solver.cpp +++ b/src/sat/ba_solver.cpp @@ -676,7 +676,6 @@ namespace sat { verbose_stream() << "alit: " << alit << "\n"; verbose_stream() << "num watch " << num_watch << "\n"); UNREACHABLE(); - exit(0); return l_undef; } @@ -1066,7 +1065,7 @@ namespace sat { uint64_t ba_solver::get_coeff(literal lit) const { int64_t c1 = get_coeff(lit.var()); - SASSERT(c1 < 0 == lit.sign()); + SASSERT((c1 < 0) == lit.sign()); int64_t c = std::abs(c1); m_overflow |= (c != c1); return static_cast(c); @@ -2606,7 +2605,6 @@ namespace sat { IF_VERBOSE(0, s().display_watches(verbose_stream())); UNREACHABLE(); - exit(1); return false; } } @@ -2839,7 +2837,7 @@ namespace sat { void ba_solver::simplify() { if (!s().at_base_lvl()) s().pop_to_base_level(); - unsigned trail_sz; + unsigned trail_sz, count = 0; do { trail_sz = s().init_trail_size(); m_simplify_change = false; @@ -2857,8 +2855,9 @@ namespace sat { cleanup_clauses(); cleanup_constraints(); update_pure(); + ++count; } - while (m_simplify_change || trail_sz < s().init_trail_size()); + while (count < 10 && (m_simplify_change || trail_sz < s().init_trail_size())); IF_VERBOSE(1, verbose_stream() << "(ba.simplify" << " :vars " << s().num_vars() - trail_sz @@ -4141,6 +4140,10 @@ namespace sat { return out << index2constraint(idx); } + std::ostream& ba_solver::display_constraint(std::ostream& out, ext_constraint_idx idx) const { + return out << index2constraint(idx); + } + void ba_solver::display(std::ostream& out, constraint const& c, bool values) const { switch (c.tag()) { case card_t: display(out, c.to_card(), values); break; diff --git a/src/sat/ba_solver.h b/src/sat/ba_solver.h index 141ca0887..dbbc48993 100644 --- a/src/sat/ba_solver.h +++ b/src/sat/ba_solver.h @@ -547,6 +547,7 @@ namespace sat { void flush_roots() override; std::ostream& display(std::ostream& out) const override; std::ostream& display_justification(std::ostream& out, ext_justification_idx idx) const override; + std::ostream& display_constraint(std::ostream& out, ext_constraint_idx idx) const override; void collect_statistics(statistics& st) const override; extension* copy(solver* s) override; extension* copy(lookahead* s, bool learned) override; diff --git a/src/sat/sat_asymm_branch.cpp b/src/sat/sat_asymm_branch.cpp index 941426321..f89b20897 100644 --- a/src/sat/sat_asymm_branch.cpp +++ b/src/sat/sat_asymm_branch.cpp @@ -44,26 +44,32 @@ namespace sat { unsigned m_elim_literals; unsigned m_elim_learned_literals; unsigned m_tr; + unsigned m_units; report(asymm_branch & a): m_asymm_branch(a), m_elim_literals(a.m_elim_literals), - m_elim_learned_literals(a.m_elim_learned_literals), - m_tr(a.m_tr) { + m_elim_learned_literals(a.m_elim_learned_literals), + m_tr(a.m_tr), + m_units(a.s.init_trail_size()) { m_watch.start(); } ~report() { m_watch.stop(); - IF_VERBOSE(SAT_VB_LVL, + IF_VERBOSE(2, unsigned num_learned = (m_asymm_branch.m_elim_learned_literals - m_elim_learned_literals); unsigned num_total = (m_asymm_branch.m_elim_literals - m_elim_literals); - verbose_stream() - << " (sat-asymm-branch :elim-literals " << (num_total - num_learned) - << " :elim-learned-literals " << num_learned - << " :hte " << (m_asymm_branch.m_tr - m_tr) - << " :cost " << m_asymm_branch.m_counter - << mem_stat() - << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); + unsigned num_units = (m_asymm_branch.s.init_trail_size() - m_units); + unsigned elim_lits = (num_total - num_learned); + unsigned tr = (m_asymm_branch.m_tr - m_tr); + verbose_stream() << " (sat-asymm-branch"; + if (elim_lits > 0) verbose_stream() << " :elim-literals " << elim_lits; + if (num_learned > 0) verbose_stream() << " :elim-learned-literals " << num_learned; + if (num_units > 0) verbose_stream() << " :units " << num_units; + if (tr > 0) verbose_stream() << " :hte " << tr; + verbose_stream() << " :cost " << m_asymm_branch.m_counter; + verbose_stream() << mem_stat(); + verbose_stream() << m_watch << ")\n";); } }; @@ -84,11 +90,11 @@ namespace sat { if (s.m_inconsistent) break; unsigned num_elim = m_elim_literals + m_tr - elim; - IF_VERBOSE(1, verbose_stream() << "(sat-asymm-branch-step :elim " << num_elim << ")\n";); + IF_VERBOSE(4, verbose_stream() << "(sat-asymm-branch-step :elim " << num_elim << ")\n";); if (num_elim == 0) break; } - IF_VERBOSE(1, if (m_elim_learned_literals > eliml0) + IF_VERBOSE(4, if (m_elim_learned_literals > eliml0) verbose_stream() << "(sat-asymm-branch :elim " << m_elim_learned_literals - eliml0 << ")\n";); return m_elim_literals > elim0; } @@ -98,7 +104,7 @@ namespace sat { unsigned elim = m_elim_literals; process(nullptr, s.m_clauses); s.propagate(false); - IF_VERBOSE(1, if (m_elim_learned_literals > eliml0) + IF_VERBOSE(4, if (m_elim_learned_literals > eliml0) verbose_stream() << "(sat-asymm-branch :elim " << m_elim_learned_literals - eliml0 << ")\n";); return m_elim_literals > elim; } @@ -342,7 +348,10 @@ namespace sat { break; default: if (!m_to_delete.contains(lit)) { - c[j++] = lit; + if (i != j) { + std::swap(c[i], c[j]); + } + j++; } break; } @@ -358,7 +367,7 @@ namespace sat { bool asymm_branch::propagate_literal(clause const& c, literal l) { SASSERT(!s.inconsistent()); TRACE("asymm_branch_detail", tout << "assigning: " << l << "\n";); - s.assign(l, justification()); + s.assign_scoped(l); s.propagate_core(false); // must not use propagate(), since check_missed_propagation may fail for c return s.inconsistent(); } @@ -406,18 +415,19 @@ namespace sat { bool asymm_branch::re_attach(scoped_detach& scoped_d, clause& c, unsigned new_sz) { VERIFY(s.m_trail.size() == s.m_qhead); - m_elim_literals += c.size() - new_sz; + unsigned old_sz = c.size(); + m_elim_literals += old_sz - new_sz; if (c.is_learned()) { - m_elim_learned_literals += c.size() - new_sz; + m_elim_learned_literals += old_sz - new_sz; } - switch(new_sz) { + switch (new_sz) { case 0: - s.set_conflict(justification()); + s.set_conflict(); return false; case 1: TRACE("asymm_branch", tout << "produced unit clause: " << c[0] << "\n";); - s.assign(c[0], justification()); + s.assign_unit(c[0]); s.propagate_core(false); scoped_d.del_clause(); return false; // check_missed_propagation() may fail, since m_clauses is not in a consistent state. @@ -430,8 +440,12 @@ namespace sat { return false; default: c.shrink(new_sz); - if (s.m_config.m_drat) s.m_drat.add(c, true); - // if (s.m_config.m_drat) s.m_drat.del(c0); // TBD + if (s.m_config.m_drat && new_sz < old_sz) { + s.m_drat.add(c, true); + c.restore(old_sz); + s.m_drat.del(c); + c.shrink(new_sz); + } return true; } } @@ -502,8 +516,8 @@ namespace sat { } void asymm_branch::collect_statistics(statistics & st) const { - st.update("elim literals", m_elim_literals); - st.update("tr", m_tr); + st.update("sat elim literals", m_elim_literals); + st.update("sat tr", m_tr); } void asymm_branch::reset_statistics() { diff --git a/src/sat/sat_big.cpp b/src/sat/sat_big.cpp index c1eeecd27..f327bc607 100644 --- a/src/sat/sat_big.cpp +++ b/src/sat/sat_big.cpp @@ -164,22 +164,23 @@ namespace sat { DEBUG_CODE(for (unsigned i = 0; i < num_lits; ++i) { VERIFY(m_left[i] < m_right[i]);}); } - // svector> big::s_del_bin; bool big::in_del(literal u, literal v) const { if (u.index() > v.index()) std::swap(u, v); - return m_del_bin.contains(std::make_pair(u, v)); + return m_del_bin[u.index()].contains(v); } void big::add_del(literal u, literal v) { if (u.index() > v.index()) std::swap(u, v); - m_del_bin.push_back(std::make_pair(u, v)); + + m_del_bin[u.index()].push_back(v); } unsigned big::reduce_tr(solver& s) { unsigned idx = 0; unsigned elim = 0; m_del_bin.reset(); + m_del_bin.reserve(s.m_watches.size()); for (watch_list & wlist : s.m_watches) { if (s.inconsistent()) break; literal u = to_literal(idx++); @@ -198,7 +199,7 @@ namespace sat { s.add_ate(~u, v); if (find_binary_watch(wlist, ~v)) { IF_VERBOSE(10, verbose_stream() << "binary: " << ~u << "\n"); - s.assign(~u, justification()); + s.assign_unit(~u); } // could turn non-learned non-binary tautology into learned binary. s.get_wlist(~v).erase(watched(~u, w.is_learned())); @@ -210,23 +211,6 @@ namespace sat { } wlist.set_end(itprev); } - -#if 0 - s_del_bin.append(m_del_bin); - IF_VERBOSE(1, - display(verbose_stream() << "Learned: " << learned() << ":"); - verbose_stream() << "del-bin\n"; - for (auto p : m_del_bin) { - verbose_stream() << p.first << " " << p.second << "\n"; - if (safe_reach(~p.first, p.second)) { - display_path(verbose_stream(), ~p.first, p.second) << " " << "\n"; - } - else { - display_path(verbose_stream(), ~p.second, p.first) << " " << "\n"; - } - } - ); -#endif s.propagate(false); return elim; } diff --git a/src/sat/sat_big.h b/src/sat/sat_big.h index 25093fd60..e682f9dfc 100644 --- a/src/sat/sat_big.h +++ b/src/sat/sat_big.h @@ -36,7 +36,7 @@ namespace sat { bool m_learned; bool m_include_cardinality; - svector> m_del_bin; + vector > m_del_bin; void init_dfs_num(); diff --git a/src/sat/sat_clause.cpp b/src/sat/sat_clause.cpp index 3cbd3015b..d27820e71 100644 --- a/src/sat/sat_clause.cpp +++ b/src/sat/sat_clause.cpp @@ -94,6 +94,11 @@ namespace sat { mark_strengthened(); } } + + void clause::restore(unsigned num_lits) { + SASSERT(num_lits <= m_capacity); + m_size = num_lits; + } bool clause::satisfied_by(model const & m) const { for (literal l : *this) { @@ -111,7 +116,7 @@ namespace sat { clause_offset clause::get_new_offset() const { unsigned o1 = m_lits[0].index(); -#if defined(_AMD64_) || defined(_M_IA64) +#if defined(__LP64__) || defined(_WIN64) if (sizeof(clause_offset) == 8) { unsigned o2 = m_lits[1].index(); return (clause_offset)o1 + (((clause_offset)o2) << 32); @@ -122,7 +127,7 @@ namespace sat { void clause::set_new_offset(clause_offset offset) { m_lits[0] = to_literal(static_cast(offset)); -#if defined(_AMD64_) || defined(_M_IA64) +#if defined(__LP64__) || defined(_WIN64) if (sizeof(offset) == 8) { m_lits[1] = to_literal(static_cast(offset >> 32)); } diff --git a/src/sat/sat_clause.h b/src/sat/sat_clause.h index f95696b3d..ca46063ac 100644 --- a/src/sat/sat_clause.h +++ b/src/sat/sat_clause.h @@ -68,6 +68,7 @@ namespace sat { bool is_learned() const { return m_learned; } void set_learned(bool l) { SASSERT(is_learned() != l); m_learned = l; } void shrink(unsigned num_lits); + void restore(unsigned num_lits); bool strengthened() const { return m_strengthened; } void mark_strengthened() { m_strengthened = true; update_approx(); } void unmark_strengthened() { m_strengthened = false; } diff --git a/src/sat/sat_cleaner.cpp b/src/sat/sat_cleaner.cpp index c0c6fabe4..093e6221f 100644 --- a/src/sat/sat_cleaner.cpp +++ b/src/sat/sat_cleaner.cpp @@ -88,12 +88,10 @@ namespace sat { CTRACE("sat_cleaner_frozen", c.frozen(), tout << c << "\n";); unsigned sz = c.size(); unsigned i = 0, j = 0; - bool sat = false; m_cleanup_counter += sz; for (; i < sz; i++) { switch (s.value(c[i])) { case l_true: - sat = true; goto end_loop; case l_false: m_elim_literals++; @@ -108,9 +106,9 @@ namespace sat { } end_loop: CTRACE("sat_cleaner_frozen", c.frozen(), - tout << "sat: " << sat << ", new_size: " << j << "\n"; + tout << "sat: " << (i < sz) << ", new_size: " << j << "\n"; tout << mk_lits_pp(j, c.begin()) << "\n";); - if (sat) { + if (i < sz) { m_elim_clauses++; s.del_clause(c); } @@ -119,34 +117,35 @@ namespace sat { CTRACE("sat_cleaner_bug", new_sz < 2, tout << "new_sz: " << new_sz << "\n"; if (c.size() > 0) tout << "unit: " << c[0] << "\n"; s.display_watches(tout);); - if (new_sz == 0) { - s.set_conflict(justification()); + switch (new_sz) { + case 0: + s.set_conflict(); s.del_clause(c); - } - else if (new_sz == 1) { - s.assign(c[0], justification()); + break; + case 1: + s.assign_unit(c[0]); s.del_clause(c); - } - else { + break; + case 2: 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()); - s.del_clause(c); + TRACE("cleanup_bug", tout << "clause became binary: " << c[0] << " " << c[1] << "\n";); + s.mk_bin_clause(c[0], c[1], c.is_learned()); + s.del_clause(c); + break; + default: + c.shrink(new_sz); + *it2 = *it; + it2++; + if (!c.frozen()) { + s.attach_clause(c); } - else { + if (s.m_config.m_drat && new_sz < sz) { + s.m_drat.add(c, true); + c.restore(sz); + s.m_drat.del(c); c.shrink(new_sz); - *it2 = *it; - it2++; - if (!c.frozen()) { - s.attach_clause(c); - } - if (s.m_config.m_drat) { - // for optimization, could also report deletion - // of previous version of clause. - s.m_drat.add(c, true); - } } + break; } } } @@ -166,12 +165,11 @@ namespace sat { } ~report() { m_watch.stop(); - IF_VERBOSE(SAT_VB_LVL, - verbose_stream() << " (sat-cleaner :elim-literals " << (m_cleaner.m_elim_literals - m_elim_literals) - << " :elim-clauses " << (m_cleaner.m_elim_clauses - m_elim_clauses) - << " :cost " << m_cleaner.m_cleanup_counter - << mk_stat(m_cleaner.s) - << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); + IF_VERBOSE(2, + verbose_stream() << " (sat-cleaner"; + verbose_stream() << " :elim-literals " << (m_cleaner.m_elim_literals - m_elim_literals); + verbose_stream() << " :elim-clauses " << (m_cleaner.m_elim_clauses - m_elim_clauses); + verbose_stream() << " :cost " << m_cleaner.m_cleanup_counter << m_watch << ")\n";); } }; @@ -192,10 +190,14 @@ namespace sat { report rpt(*this); m_last_num_units = trail_sz; m_cleanup_counter = 0; - cleanup_watches(); - cleanup_clauses(s.m_clauses); - cleanup_clauses(s.m_learned); - s.propagate(false); + do { + trail_sz = s.m_trail.size(); + cleanup_watches(); + cleanup_clauses(s.m_clauses); + cleanup_clauses(s.m_learned); + s.propagate(false); + } + while (trail_sz < s.m_trail.size()); CASSERT("cleaner_bug", s.check_invariant()); return true; } @@ -206,8 +208,8 @@ namespace sat { } void cleaner::collect_statistics(statistics & st) const { - st.update("elim clauses", m_elim_clauses); - st.update("elim literals", m_elim_literals); + st.update("sat elim clauses", m_elim_clauses); + st.update("sat elim literals", m_elim_literals); } }; diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index 423a5f532..08336b1e4 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -90,6 +90,7 @@ namespace sat { m_unit_walk = p.unit_walk(); m_unit_walk_threads = p.unit_walk_threads(); m_lookahead_simplify = p.lookahead_simplify(); + m_lookahead_double = p.lookahead_double(); m_lookahead_simplify_bca = p.lookahead_simplify_bca(); if (p.lookahead_reward() == symbol("heule_schur")) m_lookahead_reward = heule_schur_reward; @@ -123,13 +124,16 @@ namespace sat { m_lookahead_cube_psat_clause_base = p.lookahead_cube_psat_clause_base(); m_lookahead_cube_psat_trigger = p.lookahead_cube_psat_trigger(); m_lookahead_global_autarky = p.lookahead_global_autarky(); + m_lookahead_delta_fraction = p.lookahead_delta_fraction(); m_lookahead_use_learned = p.lookahead_use_learned(); - + if (m_lookahead_delta_fraction < 0 || m_lookahead_delta_fraction > 1.0) { + throw sat_param_exception("invalid value for delta fraction. It should be a number in the interval 0 to 1"); + } // These parameters are not exposed - m_next_simplify1 = _p.get_uint("next_simplify", 30000); + m_next_simplify1 = _p.get_uint("next_simplify", 90000); m_simplify_mult2 = _p.get_double("simplify_mult2", 1.5); - m_simplify_max = _p.get_uint("simplify_max", 500000); + m_simplify_max = _p.get_uint("simplify_max", 1000000); // -------------------------------- m_simplify_delay = p.simplify_delay(); @@ -162,6 +166,7 @@ namespace sat { m_drat_check_sat = p.drat_check_sat(); m_drat_file = p.drat_file(); m_drat = (m_drat_check_unsat || m_drat_file != symbol("") || m_drat_check_sat) && p.threads() == 1; + m_drat_binary = p.drat_binary(); 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 4d33225f0..b8d0ca0f5 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -128,7 +128,9 @@ namespace sat { double m_lookahead_cube_psat_clause_base; double m_lookahead_cube_psat_trigger; reward_t m_lookahead_reward; + bool m_lookahead_double; bool m_lookahead_global_autarky; + double m_lookahead_delta_fraction; bool m_lookahead_use_learned; bool m_incremental; @@ -155,6 +157,7 @@ namespace sat { bool m_core_minimize; bool m_core_minimize_partial; bool m_drat; + bool m_drat_binary; symbol m_drat_file; bool m_drat_check_unsat; bool m_drat_check_sat; diff --git a/src/sat/sat_drat.cpp b/src/sat/sat_drat.cpp index 932e9b35e..ac05b50f1 100644 --- a/src/sat/sat_drat.cpp +++ b/src/sat/sat_drat.cpp @@ -27,18 +27,26 @@ namespace sat { drat::drat(solver& s): s(s), m_out(nullptr), + m_bout(nullptr), 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()); + auto mode = s.m_config.m_drat_binary ? (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc) : std::ios_base::out; + m_out = alloc(std::ofstream, s.m_config.m_drat_file.str().c_str(), mode); + if (s.m_config.m_drat_binary) { + std::swap(m_out, m_bout); + } } } drat::~drat() { + if (m_out) m_out->flush(); + if (m_bout) m_bout->flush(); dealloc(m_out); + dealloc(m_bout); for (unsigned i = 0; i < m_proof.size(); ++i) { clause* c = m_proof[i]; if (c && (c->size() == 2 || m_status[i] == status::deleted || m_status[i] == status::external)) { @@ -64,14 +72,75 @@ namespace sat { } void drat::dump(unsigned n, literal const* c, status st) { + if (st == status::asserted || st == status::external) { + return; + } + + char buffer[10000]; + char digits[20]; // enough for storing unsigned + char* lastd = digits + sizeof(digits); + + int len = 0; + if (st == status::deleted) { + buffer[0] = 'd'; + buffer[1] = ' '; + len = 2; + } + for (unsigned i = 0; i < n; ++i) { + literal lit = c[i]; + unsigned v = lit.var(); + if (lit.sign()) buffer[len++] = '-'; + char* d = lastd; + while (v > 0) { + d--; + *d = (v % 10) + '0'; + v /= 10; + SASSERT(d > digits); + } + SASSERT(len + lastd - d < sizeof(buffer)); + memcpy(buffer + len, d, lastd - d); + len += static_cast(lastd - d); + buffer[len++] = ' '; + if (len + 50 > sizeof(buffer)) { + m_out->write(buffer, len); + len = 0; + } + } + buffer[len++] = '0'; + buffer[len++] = '\n'; + m_out->write(buffer, len); + } + + void drat::bdump(unsigned n, literal const* c, status st) { + unsigned char ch = 0; switch (st) { case status::asserted: return; - case status::external: return; // requires extension to drat format. - case status::learned: break; - case status::deleted: (*m_out) << "d "; break; + case status::external: return; + case status::learned: ch = 'a'; break; + case status::deleted: ch = 'd'; break; + default: UNREACHABLE(); break; } - for (unsigned i = 0; i < n; ++i) (*m_out) << c[i] << " "; - (*m_out) << "0\n"; + char buffer[10000]; + int len = 0; + buffer[len++] = ch; + + for (unsigned i = 0; i < n; ++i) { + literal lit = c[i]; + unsigned v = 2 * lit.var() + (lit.sign() ? 1 : 0); + do { + ch = static_cast(v & 255); + v >>= 7; + if (v) ch |= 128; + buffer[len++] = ch; + if (len == sizeof(buffer)) { + m_bout->write(buffer, len); + len = 0; + } + } + while (v); + } + buffer[len++] = 0; + m_bout->write(buffer, len); } bool drat::is_cleaned(clause& c) const { @@ -104,7 +173,13 @@ namespace sat { if (st == status::deleted) { return; } - assign_propagate(l); + if (m_check_unsat) { + assign_propagate(l); + } + + clause* c = s.alloc_clause(1, &l, st == status::learned); + m_proof.push_back(c); + m_status.push_back(st); } void drat::append(literal l1, literal l2, status st) { @@ -121,6 +196,7 @@ namespace sat { clause* c = s.alloc_clause(2, lits, st == status::learned); m_proof.push_back(c); m_status.push_back(st); + if (!m_check_unsat) return; unsigned idx = m_watched_clauses.size(); m_watched_clauses.push_back(watched_clause(c, l1, l2)); m_watches[(~l1).index()].push_back(idx); @@ -143,14 +219,14 @@ namespace sat { IF_VERBOSE(20, trace(verbose_stream(), n, c.begin(), st);); if (st == status::learned) { - verify(n, c.begin()); + verify(c); } m_status.push_back(st); m_proof.push_back(&c); if (st == status::deleted) { - del_watch(c, c[0]); - del_watch(c, c[1]); + if (n > 0) del_watch(c, c[0]); + if (n > 1) del_watch(c, c[1]); return; } unsigned num_watch = 0; @@ -215,16 +291,70 @@ namespace sat { if (!m_inconsistent) { DEBUG_CODE(validate_propagation();); } - for (unsigned i = 0; i < m_units.size(); ++i) { - SASSERT(m_assignment[m_units[i].var()] != l_undef); + DEBUG_CODE( + for (literal u : m_units) { + SASSERT(m_assignment[u.var()] != l_undef); + }); + +#if 0 + if (!m_inconsistent) { + literal_vector lits(n, c); + IF_VERBOSE(0, verbose_stream() << "not drup " << lits << "\n"); + for (unsigned v = 0; v < m_assignment.size(); ++v) { + lbool val = m_assignment[v]; + if (val != l_undef) { + IF_VERBOSE(0, verbose_stream() << literal(v, false) << " |-> " << val << "\n"); + } + } + for (clause* cp : s.m_clauses) { + clause& cl = *cp; + bool found = false; + for (literal l : cl) { + if (m_assignment[l.var()] != (l.sign() ? l_true : l_false)) { + found = true; + break; + } + } + if (!found) { + IF_VERBOSE(0, verbose_stream() << "Clause is false under assignment: " << cl << "\n"); + } + } + for (clause* cp : s.m_learned) { + clause& cl = *cp; + bool found = false; + for (literal l : cl) { + if (m_assignment[l.var()] != (l.sign() ? l_true : l_false)) { + found = true; + break; + } + } + if (!found) { + IF_VERBOSE(0, verbose_stream() << "Clause is false under assignment: " << cl << "\n"); + } + } + svector bin; + s.collect_bin_clauses(bin, true); + for (auto & b : bin) { + bool found = false; + if (m_assignment[b.first.var()] != (b.first.sign() ? l_true : l_false)) found = true; + if (m_assignment[b.second.var()] != (b.second.sign() ? l_true : l_false)) found = true; + if (!found) { + IF_VERBOSE(0, verbose_stream() << "Bin clause is false under assignment: " << b.first << " " << b.second << "\n"); + } + } + IF_VERBOSE(0, s.display(verbose_stream())); + exit(0); } +#endif for (unsigned i = num_units; i < m_units.size(); ++i) { m_assignment[m_units[i].var()] = l_undef; } - m_units.resize(num_units); + m_units.shrink(num_units); bool ok = m_inconsistent; + IF_VERBOSE(9, verbose_stream() << "is-drup " << m_inconsistent << "\n"); m_inconsistent = false; + return ok; } @@ -239,7 +369,7 @@ namespace sat { void drat::validate_propagation() const { for (unsigned i = 0; i < m_proof.size(); ++i) { status st = m_status[i]; - if (m_proof[i] && st != status::deleted) { + if (m_proof[i] && m_proof[i]->size() > 1 && st != status::deleted) { clause& c = *m_proof[i]; unsigned num_undef = 0, num_true = 0; for (unsigned j = 0; j < c.size(); ++j) { @@ -262,7 +392,7 @@ namespace sat { SASSERT(lits.size() == n); for (unsigned i = 0; i < m_proof.size(); ++i) { status st = m_status[i]; - if (m_proof[i] && (st == status::asserted || st == status::external)) { + if (m_proof[i] && m_proof[i]->size() > 1 && (st == status::asserted || st == status::external)) { clause& c = *m_proof[i]; unsigned j = 0; for (; j < c.size() && c[j] != ~l; ++j) {} @@ -279,8 +409,18 @@ namespace sat { } void drat::verify(unsigned n, literal const* c) { - if (m_check_unsat && !is_drup(n, c) && !is_drat(n, c)) { - std::cout << "Verification failed\n"; + if (!m_check_unsat) { + return; + } + for (unsigned i = 0; i < n; ++i) { + declare(c[i]); + } + if (!is_drup(n, c) && !is_drat(n, c)) { + literal_vector lits(n, c); + std::cout << "Verification of " << lits << " failed\n"; + s.display(std::cout); + SASSERT(false); + exit(0); UNREACHABLE(); //display(std::cout); TRACE("sat", @@ -288,10 +428,39 @@ namespace sat { display(tout); s.display(tout);); UNREACHABLE(); - exit(0); } } + bool drat::contains(unsigned n, literal const* lits) { + if (!m_check) return true; + for (unsigned i = m_proof.size(); i-- > 0; ) { + clause& c = *m_proof[i]; + status st = m_status[i]; + if (match(n, lits, c)) { + return st != status::deleted; + } + } + return false; + } + + bool drat::match(unsigned n, literal const* lits, clause const& c) const { + if (n == c.size()) { + for (unsigned i = 0; i < n; ++i) { + literal lit1 = lits[i]; + bool found = false; + for (literal lit2 : c) { + if (lit1 == lit2) { + found = true; + break; + } + } + if (!found) return false; + } + return true; + } + return false; + } + void drat::display(std::ostream& out) const { out << "units: " << m_units << "\n"; for (unsigned i = 0; i < m_assignment.size(); ++i) { @@ -421,22 +590,27 @@ namespace sat { void drat::add() { if (m_out) (*m_out) << "0\n"; + if (m_bout) bdump(0, nullptr, status::learned); if (m_check_unsat) { SASSERT(m_inconsistent); } } void drat::add(literal l, bool learned) { + TRACE("sat", tout << "add: " << l << " " << (learned?"l":"t") << "\n";); declare(l); status st = get_status(learned); if (m_out) dump(1, &l, st); + if (m_bout) bdump(1, &l, st); if (m_check) append(l, st); } void drat::add(literal l1, literal l2, bool learned) { + TRACE("sat", tout << "add: " << l1 << " " << l2 << " " << (learned?"l":"t") << "\n";); declare(l1); declare(l2); literal ls[2] = {l1, l2}; status st = get_status(learned); if (m_out) dump(2, ls, st); + if (m_bout) bdump(2, ls, st); if (m_check) append(l1, l2, st); } void drat::add(clause& c, bool learned) { @@ -444,6 +618,7 @@ namespace sat { 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 (m_bout) bdump(c.size(), c.begin(), st); if (m_check_unsat) append(c, get_status(learned)); } void drat::add(literal_vector const& lits, svector const& premises) { @@ -462,6 +637,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 (m_bout) bdump(c.size(), c.begin(), status::learned); if (m_check) { switch (c.size()) { case 0: add(); break; @@ -478,25 +654,39 @@ namespace sat { void drat::del(literal l) { if (m_out) dump(1, &l, status::deleted); + if (m_bout) bdump(1, &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 (m_check) - append(l1, l2, status::deleted); + if (m_bout) bdump(2, ls, status::deleted); + if (m_check) append(l1, l2, status::deleted); } + void drat::del(clause& c) { + +#if 0 + // check_duplicates: + for (literal lit : c) { + VERIFY(!m_seen[lit.index()]); + m_seen[lit.index()] = true; + } + for (literal lit : c) { + m_seen[lit.index()] = false; + } +#endif + TRACE("sat", tout << "del: " << c << "\n";); if (m_out) dump(c.size(), c.begin(), status::deleted); + if (m_bout) bdump(c.size(), c.begin(), status::deleted); if (m_check) { clause* c1 = s.alloc_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"; + void drat::check_model(model const& m) { } } diff --git a/src/sat/sat_drat.h b/src/sat/sat_drat.h index 64d796839..35e5a0655 100644 --- a/src/sat/sat_drat.h +++ b/src/sat/sat_drat.h @@ -46,6 +46,7 @@ namespace sat { typedef svector watch; solver& s; std::ostream* m_out; + std::ostream* m_bout; ptr_vector m_proof; svector m_status; literal_vector m_units; @@ -55,6 +56,7 @@ namespace sat { bool m_check_unsat, m_check_sat, m_check; void dump(unsigned n, literal const* c, status st); + void bdump(unsigned n, literal const* c, status st); void append(literal l, status st); void append(literal l1, literal l2, status st); void append(clause& c, status st); @@ -67,7 +69,6 @@ namespace sat { void propagate(literal l); void assign_propagate(literal l); void del_watch(clause& c, literal l); - void verify(unsigned n, literal const* c); bool is_drup(unsigned n, literal const* c); bool is_drat(unsigned n, literal const* c); bool is_drat(unsigned n, literal const* c, unsigned pos); @@ -75,6 +76,7 @@ namespace sat { void trace(std::ostream& out, unsigned n, literal const* c, status st); void display(std::ostream& out) const; void validate_propagation() const; + bool match(unsigned n, literal const* lits, clause const& c) const; public: drat(solver& s); @@ -93,6 +95,16 @@ namespace sat { void del(literal l1, literal l2); void del(clause& c); + void verify(clause const& c) { verify(c.size(), c.begin()); } + void verify(unsigned n, literal const* c); + void verify(literal l1, literal l2) { literal lits[2] = {l1, l2}; verify(2, lits); } + void verify(literal l1, literal l2, literal l3) { literal lits[3] = {l1, l2, l3}; verify(3, lits); } + + bool contains(clause const& c) { return contains(c.size(), c.begin()); } + bool contains(unsigned n, literal const* c); + bool contains(literal l1, literal l2) { literal lits[2] = {l1, l2}; return contains(2, lits); } + bool contains(literal l1, literal l2, literal l3) { literal lits[3] = {l1, l2, l3}; return contains(3, lits); } + void check_model(model const& m); }; diff --git a/src/sat/sat_elim_eqs.cpp b/src/sat/sat_elim_eqs.cpp index 870aa7fe2..bc69c1f5d 100644 --- a/src/sat/sat_elim_eqs.cpp +++ b/src/sat/sat_elim_eqs.cpp @@ -23,9 +23,15 @@ Revision History: namespace sat { elim_eqs::elim_eqs(solver & s): - m_solver(s) { + m_solver(s), + m_to_delete(nullptr) { } + elim_eqs::~elim_eqs() { + dealloc(m_to_delete); + } + + inline literal norm(literal_vector const & roots, literal l) { if (l.sign()) return ~roots[l.var()]; @@ -47,7 +53,7 @@ namespace sat { literal l2 = it->get_literal(); literal r2 = norm(roots, l2); if (r1 == r2) { - m_solver.assign(r1, justification()); + m_solver.assign_unit(r1); if (m_solver.inconsistent()) return; // consume unit @@ -86,6 +92,12 @@ namespace sat { m_new_bin.reset(); } + void elim_eqs::drat_delete_clause() { + if (m_solver.m_config.m_drat) { + m_solver.m_drat.del(*m_to_delete->get()); + } + } + void elim_eqs::cleanup_clauses(literal_vector const & roots, clause_vector & cs) { clause_vector::iterator it = cs.begin(); clause_vector::iterator it2 = it; @@ -107,8 +119,16 @@ namespace sat { it2++; continue; } - if (!c.frozen()) + if (!c.frozen()) { m_solver.detach_clause(c); + } + + // save clause to be deleted for drat + if (m_solver.m_config.m_drat) { + if (!m_to_delete) m_to_delete = alloc(tmp_clause); + m_to_delete->set(sz, c.begin(), c.is_learned()); + } + // apply substitution for (i = 0; i < sz; i++) { literal lit = c[i]; @@ -124,60 +144,72 @@ namespace sat { CTRACE("sat", l != norm(roots, l), tout << l << " " << norm(roots, l) << "\n"; tout.flush();); SASSERT(l == norm(roots, l)); } }); + // remove duplicates, and check if it is a tautology - literal l_prev = null_literal; unsigned j = 0; + literal l_prev = null_literal; for (i = 0; i < sz; i++) { literal l = c[i]; - if (l == l_prev) - continue; - if (l == ~l_prev) + if (l == ~l_prev) { break; + } + if (l == l_prev) { + continue; + } + SASSERT(l != ~l_prev); l_prev = l; lbool val = m_solver.value(l); - if (val == l_true) - break; // clause was satisfied - if (val == l_false) + if (val == l_true) { + break; + } + if (val == l_false) { continue; // skip + } c[j] = l; j++; } + TRACE("elim_eqs", tout << "after removing duplicates: " << c << " j: " << j << "\n";); + if (i < sz) { - // clause is a tautology or was simplified to true + drat_delete_clause(); + c.set_removed(true); m_solver.del_clause(c); continue; } - if (j == 0) { - // empty clause - m_solver.set_conflict(justification()); + + switch (j) { + case 0: + m_solver.set_conflict(); for (; it != end; ++it) { *it2 = *it; it2++; } cs.set_end(it2); - return; - } - TRACE("elim_eqs", tout << "after removing duplicates: " << c << " j: " << j << "\n";); - - SASSERT(j >= 1); - switch (j) { + return; case 1: - m_solver.assign(c[0], justification()); + m_solver.assign_unit(c[0]); + drat_delete_clause(); + c.set_removed(true); m_solver.del_clause(c); break; case 2: m_solver.mk_bin_clause(c[0], c[1], c.is_learned()); + drat_delete_clause(); + c.set_removed(true); m_solver.del_clause(c); break; default: SASSERT(*it == &c); if (j < sz) { - if (m_solver.m_config.m_drat) m_solver.m_drat.del(c); c.shrink(j); - if (m_solver.m_config.m_drat) m_solver.m_drat.add(c, true); } - else + else { c.update_approx(); + } + if (m_solver.m_config.m_drat) { + m_solver.m_drat.add(c, true); + drat_delete_clause(); + } DEBUG_CODE(for (literal l : c) VERIFY(l == norm(roots, l));); diff --git a/src/sat/sat_elim_eqs.h b/src/sat/sat_elim_eqs.h index 143fcbb3f..ac132b213 100644 --- a/src/sat/sat_elim_eqs.h +++ b/src/sat/sat_elim_eqs.h @@ -23,6 +23,7 @@ Revision History: namespace sat { class solver; + class tmp_clause; class elim_eqs { struct bin { @@ -32,6 +33,8 @@ namespace sat { }; svector m_new_bin; solver & m_solver; + tmp_clause* m_to_delete; + void drat_delete_clause(); void save_elim(literal_vector const & roots, bool_var_vector const & to_elim); void cleanup_clauses(literal_vector const & roots, clause_vector & cs); void cleanup_bin_watches(literal_vector const & roots); @@ -39,6 +42,7 @@ namespace sat { bool check_clause(clause const& c, literal_vector const& roots) const; public: elim_eqs(solver & s); + ~elim_eqs(); void operator()(literal_vector const & roots, bool_var_vector const & to_elim); }; diff --git a/src/sat/sat_elim_vars.cpp b/src/sat/sat_elim_vars.cpp index 299fbace1..1759ec2ad 100644 --- a/src/sat/sat_elim_vars.cpp +++ b/src/sat/sat_elim_vars.cpp @@ -167,10 +167,9 @@ namespace sat{ if (simp.cleanup_clause(c)) return; - if (v0 == 39063) IF_VERBOSE(0, verbose_stream() << "bdd: " << c << "\n"); switch (c.size()) { case 0: - s.set_conflict(justification()); + s.set_conflict(); break; case 1: simp.propagate_unit(c[0]); diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index 41aebb97e..446569e84 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -70,6 +70,7 @@ namespace sat { virtual lbool get_phase(bool_var v) = 0; virtual std::ostream& display(std::ostream& out) const = 0; virtual std::ostream& display_justification(std::ostream& out, ext_justification_idx idx) const = 0; + virtual std::ostream& display_constraint(std::ostream& out, ext_constraint_idx idx) const = 0; virtual void collect_statistics(statistics& st) const = 0; virtual extension* copy(solver* s) = 0; virtual extension* copy(lookahead* s, bool learned) = 0; diff --git a/src/sat/sat_local_search.cpp b/src/sat/sat_local_search.cpp index b65abfd23..cdb90e2a0 100644 --- a/src/sat/sat_local_search.cpp +++ b/src/sat/sat_local_search.cpp @@ -26,7 +26,8 @@ Notes: namespace sat { void local_search::init() { - + flet _init(m_initializing, true); + m_unsat_stack.reset(); for (unsigned i = 0; i < m_assumptions.size(); ++i) { add_clause(1, m_assumptions.c_ptr() + i); } @@ -37,7 +38,7 @@ namespace sat { if (m_config.phase_sticky()) { for (var_info& vi : m_vars) if (!vi.m_unit) - vi.m_value = vi.m_bias < 100; + vi.m_value = vi.m_bias > 50; } else { for (var_info& vi : m_vars) @@ -45,42 +46,14 @@ namespace sat { vi.m_value = (0 == (m_rand() % 2)); } - m_best_solution.resize(num_vars() + 1, false); m_index_in_unsat_stack.resize(num_constraints(), 0); - coefficient_in_ob_constraint.resize(num_vars() + 1, 0); - - if (m_config.mode() == local_search_mode::gsat) { - uint_set is_neighbor; - for (bool_var v = 0; v < num_vars(); ++v) { - is_neighbor.reset(); - bool pol = true; - var_info& vi = m_vars[v]; - for (unsigned k = 0; k < 2; pol = !pol, k++) { - for (auto const& wi : m_vars[v].m_watch[pol]) { - constraint const& c = m_constraints[wi.m_constraint_id]; - for (literal lit : c) { - bool_var w = lit.var(); - if (w == v || is_neighbor.contains(w)) continue; - is_neighbor.insert(w); - vi.m_neighbors.push_back(w); - } - } - } - } - } - - for (auto const& c : ob_constraint) { - coefficient_in_ob_constraint[c.var_id] = c.coefficient; - } - set_parameters(); } void local_search::init_cur_solution() { for (var_info& vi : m_vars) { - // use bias with a small probability if (!vi.m_unit) { - if (m_rand() % 10 < 5 || m_config.phase_sticky()) { + if (m_config.phase_sticky()) { vi.m_value = ((unsigned)(m_rand() % 100) < vi.m_bias); } else { @@ -116,7 +89,6 @@ namespace sat { coeff_vector& falsep = m_vars[v].m_watch[!is_true]; for (auto const& coeff : falsep) { constraint& c = m_constraints[coeff.m_constraint_id]; - //SASSERT(falsep[i].m_coeff == 1); // will --slack if (c.m_slack <= 0) { dec_slack_score(v); @@ -125,7 +97,6 @@ namespace sat { } } for (auto const& coeff : truep) { - //SASSERT(coeff.m_coeff == 1); constraint& c = m_constraints[coeff.m_constraint_id]; // will --true_terms_count[c] // will ++slack @@ -151,22 +122,19 @@ namespace sat { void local_search::reinit() { - IF_VERBOSE(1, verbose_stream() << "(sat-local-search reinit)\n";); - if (true || !m_is_pb) { - // - // the following methods does NOT converge for pseudo-boolean - // can try other way to define "worse" and "better" - // the current best noise is below 1000 - // - if (m_best_unsat_rate > m_last_best_unsat_rate) { - // worse - m_noise -= m_noise * 2 * m_noise_delta; - m_best_unsat_rate *= 1000.0; - } - else { - // better - m_noise += (10000 - m_noise) * m_noise_delta; - } + // + // the following methods does NOT converge for pseudo-boolean + // can try other way to define "worse" and "better" + // the current best noise is below 1000 + // + if (m_best_unsat_rate > m_last_best_unsat_rate) { + // worse + m_noise -= m_noise * 2 * m_noise_delta; + m_best_unsat_rate *= 1000.0; + } + else { + // better + m_noise += (10000 - m_noise) * m_noise_delta; } for (constraint & c : m_constraints) { @@ -186,11 +154,9 @@ namespace sat { m_vars.back().m_score = INT_MIN; m_vars.back().m_conf_change = false; m_vars.back().m_slack_score = INT_MIN; - m_vars.back().m_cscc = 0; m_vars.back().m_time_stamp = m_max_steps + 1; for (unsigned i = 0; i < num_vars(); ++i) { m_vars[i].m_time_stamp = 0; - m_vars[i].m_cscc = 1; m_vars[i].m_conf_change = true; m_vars[i].m_in_goodvar_stack = false; m_vars[i].m_score = 0; @@ -208,6 +174,7 @@ namespace sat { if (m_is_unsat) { IF_VERBOSE(0, verbose_stream() << "unsat during reinit\n"); } + DEBUG_CODE(verify_slack();); } bool local_search::propagate(literal lit) { @@ -230,9 +197,9 @@ namespace sat { return false; } if (unit) { - for (literal lit : m_prop_queue) { - VERIFY(is_true(lit)); - add_unit(lit); + for (literal lit2 : m_prop_queue) { + VERIFY(is_true(lit2)); + add_unit(lit2, lit); } } return true; @@ -253,32 +220,7 @@ namespace sat { constraint const& c = m_constraints[m_unsat_stack[0]]; IF_VERBOSE(2, display(verbose_stream() << "single unsat:", c)); } - } - - void local_search::calculate_and_update_ob() { - unsigned i, v; - int objective_value = 0; - for (i = 0; i < ob_constraint.size(); ++i) { - v = ob_constraint[i].var_id; - if (cur_solution(v)) - objective_value += ob_constraint[i].coefficient; - } - if (objective_value > m_best_objective_value) { - m_best_solution.reset(); - for (unsigned v = 0; v < num_vars(); ++v) { - m_best_solution.push_back(cur_solution(v)); - } - m_best_objective_value = objective_value; - } - } - - bool local_search::all_objectives_are_met() const { - for (unsigned i = 0; i < ob_constraint.size(); ++i) { - bool_var v = ob_constraint[i].var_id; - if (!cur_solution(v)) return false; - } - return true; - } + } void local_search::verify_solution() const { IF_VERBOSE(0, verbose_stream() << "verifying solution\n"); @@ -289,10 +231,24 @@ namespace sat { void local_search::verify_unsat_stack() const { for (unsigned i : m_unsat_stack) { constraint const& c = m_constraints[i]; + if (c.m_k >= constraint_value(c)) { + IF_VERBOSE(0, display(verbose_stream() << i << " ", c) << "\n"); + IF_VERBOSE(0, verbose_stream() << "units " << m_units << "\n"); + } VERIFY(c.m_k < constraint_value(c)); } } + bool local_search::verify_goodvar() const { + unsigned g = 0; + for (unsigned v = 0; v < num_vars(); ++v) { + if (conf_change(v) && score(v) > 0) { + ++g; + } + } + return g == m_goodvar_stack.size(); + } + unsigned local_search::constraint_coeff(constraint const& c, literal l) const { for (auto const& pb : m_vars[l.var()].m_watch[is_pos(l)]) { if (pb.m_constraint_id == c.m_id) return pb.m_coeff; @@ -301,6 +257,24 @@ namespace sat { return 0; } + void local_search::verify_constraint(constraint const& c) const { + unsigned value = constraint_value(c); + IF_VERBOSE(11, display(verbose_stream() << "verify ", c);); + TRACE("sat", display(verbose_stream() << "verify ", c);); + if (c.m_k < value) { + IF_VERBOSE(0, display(verbose_stream() << "violated constraint: ", c) << "value: " << value << "\n";); + } + } + + void local_search::verify_slack(constraint const& c) const { + VERIFY(constraint_value(c) + c.m_slack == c.m_k); + } + + void local_search::verify_slack() const { + for (constraint const& c : m_constraints) { + verify_slack(c); + } + } unsigned local_search::constraint_value(constraint const& c) const { unsigned value = 0; @@ -311,15 +285,6 @@ namespace sat { } return value; } - - void local_search::verify_constraint(constraint const& c) const { - unsigned value = constraint_value(c); - IF_VERBOSE(11, display(verbose_stream() << "verify ", c);); - TRACE("sat", display(verbose_stream() << "verify ", c);); - if (c.m_k < value) { - IF_VERBOSE(0, display(verbose_stream() << "violated constraint: ", c) << "value: " << value << "\n";); - } - } void local_search::add_clause(unsigned sz, literal const* c) { add_cardinality(sz, c, sz - 1); @@ -328,7 +293,7 @@ namespace sat { // ~c <= k void local_search::add_cardinality(unsigned sz, literal const* c, unsigned k) { if (sz == 1 && k == 0) { - add_unit(c[0]); + add_unit(c[0], null_literal); return; } if (k == 1 && sz == 2) { @@ -353,7 +318,7 @@ namespace sat { // c * coeffs <= k void local_search::add_pb(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k) { if (sz == 1 && k == 0) { - add_unit(~c[0]); + add_unit(~c[0], null_literal); return; } unsigned id = m_constraints.size(); @@ -366,15 +331,19 @@ namespace sat { } } - void local_search::add_unit(literal lit) { + void local_search::add_unit(literal lit, literal exp) { bool_var v = lit.var(); if (is_unit(lit)) return; - VERIFY(!m_units.contains(v)); - m_vars[v].m_bias = lit.sign() ? 0 : 100; + SASSERT(!m_units.contains(v)); + if (m_vars[v].m_value == lit.sign() && !m_initializing) { + flip_walksat(v); + } m_vars[v].m_value = !lit.sign(); + m_vars[v].m_bias = lit.sign() ? 0 : 100; m_vars[v].m_unit = true; + m_vars[v].m_explain = exp; m_units.push_back(v); - verify_unsat_stack(); + DEBUG_CODE(verify_unsat_stack();); } local_search::local_search() : @@ -383,19 +352,19 @@ namespace sat { } void local_search::import(solver& s, bool _init) { + flet linit(m_initializing, true); m_is_pb = false; m_vars.reset(); m_constraints.reset(); m_units.reset(); m_unsat_stack.reset(); - m_vars.reserve(s.num_vars()); + m_config.set_config(s.get_config()); + if (m_config.phase_sticky()) { unsigned v = 0; for (var_info& vi : m_vars) { - if (!vi.m_unit) - vi.m_bias = s.m_phase[v] == POS_PHASE ? 100 : 0; - ++v; + vi.m_bias = s.m_phase[v++] == POS_PHASE ? 98 : 2; } } @@ -524,9 +493,6 @@ namespace sat { local_search::~local_search() { } - void local_search::add_soft(bool_var v, int weight) { - ob_constraint.push_back(ob_term(v, weight)); - } lbool local_search::check() { return check(0, nullptr); @@ -534,7 +500,7 @@ namespace sat { #define PROGRESS(tries, flips) \ if (tries % 10 == 0 || m_unsat_stack.empty()) { \ - IF_VERBOSE(1, verbose_stream() << "(sat-local-search" \ + IF_VERBOSE(1, verbose_stream() << "(sat.local-search" \ << " :flips " << flips \ << " :noise " << m_noise \ << " :unsat " << /*m_unsat_stack.size()*/ m_best_unsat \ @@ -547,10 +513,9 @@ namespace sat { m_last_best_unsat_rate = 1; reinit(); + DEBUG_CODE(verify_slack();); timer timer; - timer.start(); unsigned step = 0, total_flips = 0, tries = 0; - PROGRESS(tries, total_flips); for (tries = 1; !m_unsat_stack.empty() && m_limit.inc(); ++tries) { ++m_stats.m_num_restarts; @@ -572,39 +537,7 @@ namespace sat { reinit(); } } - } - - void local_search::gsat() { - reinit(); - bool_var flipvar; - timer timer; - timer.start(); - unsigned tries, step = 0, total_flips = 0; - for (tries = 1; m_limit.inc() && !m_unsat_stack.empty(); ++tries) { - reinit(); - for (step = 1; step <= m_max_steps; ) { - // feasible - if (m_unsat_stack.empty()) { - calculate_and_update_ob(); - if (m_best_objective_value >= m_best_known_value) { - break; - } - } - if (m_unsat_stack.size() < m_best_unsat) { - set_best_unsat(); - } - flipvar = pick_var_gsat(); - flip_gsat(flipvar); - m_vars[flipvar].m_time_stamp = step++; - } - total_flips += step; - PROGRESS(tries, total_flips); - - // tell the SAT solvers about the phase of variables. - if (m_par && tries % 10 == 0) { - m_par->get_phase(*this); - } - } + PROGRESS(0, total_flips); } lbool local_search::check(unsigned sz, literal const* assumptions, parallel* p) { @@ -612,28 +545,24 @@ namespace sat { m_model.reset(); m_assumptions.reset(); m_assumptions.append(sz, assumptions); + unsigned num_units = m_units.size(); init(); - - switch (m_config.mode()) { - case local_search_mode::gsat: - gsat(); - break; - case local_search_mode::wsat: - walksat(); - break; + walksat(); + + // remove unit clauses + for (unsigned i = m_units.size(); i-- > num_units; ) { + m_vars[m_units[i]].m_unit = false; } - - // remove unit clauses from assumptions. - m_constraints.shrink(num_constraints() - sz); + m_units.shrink(num_units); + m_vars.pop_back(); // remove sentinel variable TRACE("sat", display(tout);); lbool result; if (m_is_unsat) { - // result = l_false; - result = l_undef; + result = l_false; } - else if (m_unsat_stack.empty() && all_objectives_are_met()) { + else if (m_unsat_stack.empty()) { verify_solution(); extract_model(); result = l_true; @@ -641,7 +570,7 @@ namespace sat { else { result = l_undef; } - IF_VERBOSE(1, verbose_stream() << "(sat-local-search " << result << ")\n";); + IF_VERBOSE(1, verbose_stream() << "(sat.local-search " << result << ")\n";); IF_VERBOSE(20, display(verbose_stream());); return result; } @@ -661,18 +590,40 @@ namespace sat { m_unsat_stack.push_back(c); } + void local_search::pick_flip_lookahead() { + unsigned num_unsat = m_unsat_stack.size(); + constraint const& c = m_constraints[m_unsat_stack[m_rand() % num_unsat]]; + literal best = null_literal; + unsigned best_make = UINT_MAX; + for (literal lit : c.m_literals) { + if (!is_unit(lit) && is_true(lit)) { + flip_walksat(lit.var()); + if (propagate(~lit) && best_make > m_unsat_stack.size()) { + best = lit; + best_make = m_unsat_stack.size(); + } + flip_walksat(lit.var()); + propagate(lit); + } + } + if (best != null_literal) { + flip_walksat(best.var()); + propagate(~best); + } + else { + std::cout << "no best\n"; + } + } + void local_search::pick_flip_walksat() { reflip: bool_var best_var = null_bool_var; unsigned n = 1; bool_var v = null_bool_var; unsigned num_unsat = m_unsat_stack.size(); - constraint const& c = m_constraints[m_unsat_stack[m_rand() % m_unsat_stack.size()]]; - // VERIFY(c.m_k < constraint_value(c)); + constraint const& c = m_constraints[m_unsat_stack[m_rand() % num_unsat]]; unsigned reflipped = 0; bool is_core = m_unsat_stack.size() <= 10; - // TBD: dynamic noise strategy - //if (m_rand() % 100 < 98) { if (m_rand() % 10000 <= m_noise) { // take this branch with 98% probability. // find the first one, to fast break the rest @@ -763,7 +714,7 @@ namespace sat { if (is_true(lit)) { flip_walksat(best_var); } - add_unit(~lit); + add_unit(~lit, null_literal); if (!propagate(~lit)) { IF_VERBOSE(0, verbose_stream() << "unsat\n"); m_is_unsat = true; @@ -779,6 +730,7 @@ namespace sat { } void local_search::flip_walksat(bool_var flipvar) { + ++m_stats.m_num_flips; VERIFY(!is_unit(flipvar)); m_vars[flipvar].m_value = !cur_solution(flipvar); @@ -794,6 +746,7 @@ namespace sat { constraint& c = m_constraints[ci]; int old_slack = c.m_slack; c.m_slack -= pbc.m_coeff; + DEBUG_CODE(verify_slack(c);); if (c.m_slack < 0 && old_slack >= 0) { // from non-negative to negative: sat -> unsat unsat(ci); } @@ -803,220 +756,21 @@ namespace sat { constraint& c = m_constraints[ci]; int old_slack = c.m_slack; c.m_slack += pbc.m_coeff; + DEBUG_CODE(verify_slack(c);); if (c.m_slack >= 0 && old_slack < 0) { // from negative to non-negative: unsat -> sat sat(ci); } } - // verify_unsat_stack(); - } - - void local_search::flip_gsat(bool_var flipvar) { - // already changed truth value!!!! - m_vars[flipvar].m_value = !cur_solution(flipvar); - - unsigned v; - int org_flipvar_score = score(flipvar); - int org_flipvar_slack_score = slack_score(flipvar); - - bool flip_is_true = cur_solution(flipvar); - coeff_vector& truep = m_vars[flipvar].m_watch[flip_is_true]; - coeff_vector& falsep = m_vars[flipvar].m_watch[!flip_is_true]; - - // update related clauses and neighbor vars - for (unsigned i = 0; i < truep.size(); ++i) { - constraint & c = m_constraints[truep[i].m_constraint_id]; - //++true_terms_count[c]; - --c.m_slack; - switch (c.m_slack) { - case -2: // from -1 to -2 - for (literal l : c) { - v = l.var(); - // flipping the slack increasing var will no longer satisfy this constraint - if (is_true(l)) { - //score[v] -= constraint_weight[c]; - dec_score(v); - } - } - break; - case -1: // from 0 to -1: sat -> unsat - for (literal l : c) { - v = l.var(); - inc_cscc(v); - //score[v] += constraint_weight[c]; - inc_score(v); - // slack increasing var - if (is_true(l)) - inc_slack_score(v); - } - unsat(truep[i].m_constraint_id); - break; - case 0: // from 1 to 0 - for (literal l : c) { - v = l.var(); - // flip the slack decreasing var will falsify this constraint - if (is_false(l)) { - // score[v] -= constraint_weight[c]; - dec_score(v); - dec_slack_score(v); - } - } - break; - default: - break; - } - } - for (pbcoeff const& f : falsep) { - constraint& c = m_constraints[f.m_constraint_id]; - //--true_terms_count[c]; - ++c.m_slack; - switch (c.m_slack) { - case 1: // from 0 to 1 - for (literal l : c) { - v = l.var(); - // flip the slack decreasing var will no long falsify this constraint - if (is_false(l)) { - //score[v] += constraint_weight[c]; - inc_score(v); - inc_slack_score(v); - } - } - break; - case 0: // from -1 to 0: unsat -> sat - for (literal l : c) { - v = l.var(); - inc_cscc(v); - //score[v] -= constraint_weight[c]; - dec_score(v); - // slack increasing var no longer sat this var - if (is_true(l)) - dec_slack_score(v); - } - sat(f.m_constraint_id); - break; - case -1: // from -2 to -1 - for (literal l : c) { - v = l.var(); - // flip the slack increasing var will satisfy this constraint - if (is_true(l)) { - //score[v] += constraint_weight[c]; - inc_score(v); - } - } - break; - default: - break; - } - } - m_vars[flipvar].m_score = -org_flipvar_score; - m_vars[flipvar].m_slack_score = -org_flipvar_slack_score; - m_vars[flipvar].m_conf_change = false; - m_vars[flipvar].m_cscc = 0; - - /* update CCD */ - // remove the vars no longer goodvar in goodvar stack - for (unsigned i = m_goodvar_stack.size(); i > 0;) { - --i; - v = m_goodvar_stack[i]; - if (score(v) <= 0) { - m_goodvar_stack[i] = m_goodvar_stack.back(); - m_goodvar_stack.pop_back(); - m_vars[v].m_in_goodvar_stack = false; - } - } - // update all flipvar's neighbor's conf_change to true, add goodvar/okvar - - var_info& vi = m_vars[flipvar]; - for (auto v : vi.m_neighbors) { - m_vars[v].m_conf_change = true; - if (score(v) > 0 && !already_in_goodvar_stack(v)) { - m_goodvar_stack.push_back(v); - m_vars[v].m_in_goodvar_stack = true; - } - } - } - - bool local_search::tie_breaker_sat(bool_var v, bool_var best_var) { - // most improvement on objective value - int v_imp = cur_solution(v) ? -coefficient_in_ob_constraint.get(v, 0) : coefficient_in_ob_constraint.get(v, 0); - int b_imp = cur_solution(best_var) ? -coefficient_in_ob_constraint.get(best_var, 0) : coefficient_in_ob_constraint.get(best_var, 0); - // std::cout << v_imp << "\n"; - // break tie 1: max imp - // break tie 2: conf_change - // break tie 3: time_stamp - - return - (v_imp > b_imp) || - ((v_imp == b_imp) && - ((conf_change(v) && !conf_change(best_var)) || - ((conf_change(v) == conf_change(best_var)) && - (time_stamp(v) < time_stamp(best_var))))); - } - - bool local_search::tie_breaker_ccd(bool_var v, bool_var best_var) { - // break tie 1: max score - // break tie 2: max slack_score - // break tie 3: cscc - // break tie 4: oldest one - return - ((score(v) > score(best_var)) || - ((score(v) == score(best_var)) && - ((slack_score(v) > slack_score(best_var)) || - ((slack_score(v) == slack_score(best_var)) && - ((cscc(v) > cscc(best_var)) || - ((cscc(v) == cscc(best_var)) && - (time_stamp(v) < time_stamp(best_var)))))))); - } - - bool_var local_search::pick_var_gsat() { - bool_var best_var = m_vars.size()-1; // sentinel variable - // SAT Mode - if (m_unsat_stack.empty()) { - //std::cout << "as\t"; - for (auto const& c : ob_constraint) { - bool_var v = c.var_id; - if (tie_breaker_sat(v, best_var)) - best_var = v; - } - return best_var; - } - - // Unsat Mode: CCD > RD - // CCD mode - if (!m_goodvar_stack.empty()) { - //++ccd; - best_var = m_goodvar_stack[0]; - for (bool_var v : m_goodvar_stack) { - if (tie_breaker_ccd(v, best_var)) - best_var = v; - } - return best_var; - } - - // Diversification Mode - constraint const& c = m_constraints[m_unsat_stack[m_rand() % m_unsat_stack.size()]]; // a random unsat constraint - // Within c, from all slack increasing var, choose the oldest one - for (literal l : c) { - bool_var v = l.var(); - if (is_true(l) && time_stamp(v) < time_stamp(best_var)) - best_var = v; - } - return best_var; + DEBUG_CODE(verify_unsat_stack();); } void local_search::set_parameters() { m_rand.set_seed(m_config.random_seed()); m_best_known_value = m_config.best_known_value(); - switch (m_config.mode()) { - case local_search_mode::gsat: - m_max_steps = 2 * num_vars(); - break; - case local_search_mode::wsat: - m_max_steps = std::min(static_cast(20 * num_vars()), static_cast(1 << 17)); // cut steps off at 100K - break; - } - + m_max_steps = std::min(static_cast(20 * num_vars()), static_cast(1 << 17)); // cut steps off at 100K + TRACE("sat", tout << "seed:\t" << m_config.random_seed() << '\n'; tout << "best_known_value:\t" << m_config.best_known_value() << '\n'; @@ -1062,7 +816,9 @@ namespace sat { } std::ostream& local_search::display(std::ostream& out, unsigned v, var_info const& vi) const { - return out << "v" << v << " := " << (vi.m_value?"true":"false") << " bias: " << vi.m_bias << "\n"; + out << "v" << v << " := " << (vi.m_value?"true":"false") << " bias: " << vi.m_bias; + if (vi.m_unit) out << " u " << vi.m_explain; + return out << "\n"; } void local_search::collect_statistics(statistics& st) const { @@ -1078,26 +834,6 @@ namespace sat { } - bool local_search::check_goodvar() { - unsigned g = 0; - for (unsigned v = 0; v < num_vars(); ++v) { - if (conf_change(v) && score(v) > 0) { - ++g; - if (!already_in_goodvar_stack(v)) - std::cout << "3\n"; - } - } - if (g == m_goodvar_stack.size()) - return true; - else { - if (g < m_goodvar_stack.size()) - std::cout << "1\n"; - else - std::cout << "2\n"; // delete too many - return false; - } - } - void local_search::set_phase(bool_var v, lbool f) { unsigned& bias = m_vars[v].m_bias; if (f == l_true && bias < 100) bias++; @@ -1105,4 +841,13 @@ namespace sat { // f == l_undef ? } + void local_search::set_bias(bool_var v, lbool f) { + switch (f) { + case l_true: m_vars[v].m_bias = 99; break; + case l_false: m_vars[v].m_bias = 1; break; + default: break; + } + + } + } diff --git a/src/sat/sat_local_search.h b/src/sat/sat_local_search.h index 849b4f26b..00c38e481 100644 --- a/src/sat/sat_local_search.h +++ b/src/sat/sat_local_search.h @@ -36,6 +36,16 @@ namespace sat { local_search_mode m_mode; bool m_phase_sticky; bool m_dbg_flips; + + friend class local_search; + + void set_config(config const& cfg) { + m_mode = cfg.m_local_search_mode; + m_random_seed = cfg.m_random_seed; + m_phase_sticky = cfg.m_phase_sticky; + m_dbg_flips = cfg.m_local_search_dbg_flips; + } + public: local_search_config() { m_random_seed = 0; @@ -54,12 +64,6 @@ namespace sat { void set_random_seed(unsigned s) { m_random_seed = s; } void set_best_known_value(unsigned v) { m_best_known_value = v; } - void set_config(config const& cfg) { - m_mode = cfg.m_local_search_mode; - m_random_seed = cfg.m_random_seed; - m_phase_sticky = cfg.m_phase_sticky; - m_dbg_flips = cfg.m_local_search_dbg_flips; - } }; @@ -74,12 +78,6 @@ namespace sat { typedef svector bool_vector; typedef svector coeff_vector; - // data structure for a term in objective function - struct ob_term { - bool_var var_id; // variable id, begin with 1 - int coefficient; // non-zero integer - ob_term(bool_var v, int c): var_id(v), coefficient(c) {} - }; struct stats { unsigned m_num_flips; @@ -93,12 +91,12 @@ namespace sat { unsigned m_bias; // bias for current solution in percentage. // if bias is 0, then value is always false, if 100, then always true bool m_unit; // is this a unit literal + literal m_explain; // explanation for unit assignment bool m_conf_change; // whether its configure changes since its last flip bool m_in_goodvar_stack; int m_score; int m_slack_score; int m_time_stamp; // the flip time stamp - int m_cscc; // how many times its constraint state configure changes since its last flip bool_var_vector m_neighbors; // neighborhood variables coeff_vector m_watch[2]; literal_vector m_bin[2]; @@ -112,7 +110,6 @@ namespace sat { m_in_goodvar_stack(false), m_score(0), m_slack_score(0), - m_cscc(0), m_flips(0), m_slow_break(1e-5) {} @@ -132,19 +129,44 @@ namespace sat { literal const* end() const { return m_literals.end(); } }; - stats m_stats; - local_search_config m_config; - - // objective function: maximize - svector ob_constraint; // the objective function *constraint*, sorted in descending order - - // information about the variable - int_vector coefficient_in_ob_constraint; // var! initialized to be 0 + stats m_stats; + local_search_config m_config; + vector m_vars; // variables + svector m_units; // unit clauses + vector m_constraints; // all constraints + literal_vector m_assumptions; // temporary assumptions + literal_vector m_prop_queue; // propagation queue + unsigned m_num_non_binary_clauses; + bool m_is_pb; + bool m_is_unsat; + unsigned_vector m_unsat_stack; // store all the unsat constraints + unsigned_vector m_index_in_unsat_stack; // which position is a constraint in the unsat_stack + + // configuration changed decreasing variables (score>0 and conf_change==true) + bool_var_vector m_goodvar_stack; + bool m_initializing; + + + // information about solution + unsigned m_best_unsat; + double m_best_unsat_rate; + double m_last_best_unsat_rate; + // for non-known instance, set as maximal + int m_best_known_value = INT_MAX; // best known value for this instance + + unsigned m_max_steps = (1 << 30); + + // dynamic noise + double m_noise = 9800; // normalized by 10000 + double m_noise_delta = 0.05; + + reslimit m_limit; + random_gen m_rand; + parallel* m_par; + model m_model; - vector m_vars; - svector m_units; inline int score(bool_var v) const { return m_vars[v].m_score; } inline void inc_score(bool_var v) { m_vars[v].m_score++; } @@ -157,21 +179,10 @@ namespace sat { inline bool already_in_goodvar_stack(bool_var v) const { return m_vars[v].m_in_goodvar_stack; } inline bool conf_change(bool_var v) const { return m_vars[v].m_conf_change; } inline int time_stamp(bool_var v) const { return m_vars[v].m_time_stamp; } - inline int cscc(bool_var v) const { return m_vars[v].m_cscc; } - inline void inc_cscc(bool_var v) { m_vars[v].m_cscc++; } - - inline bool cur_solution(bool_var v) const { return m_vars[v].m_value; } inline void set_best_unsat(); /* TBD: other scores */ - vector m_constraints; - - literal_vector m_assumptions; - literal_vector m_prop_queue; - - unsigned m_num_non_binary_clauses; - bool m_is_pb; inline bool is_pos(literal t) const { return !t.sign(); } inline bool is_true(bool_var v) const { return cur_solution(v); } @@ -182,101 +193,38 @@ namespace sat { unsigned num_constraints() const { return m_constraints.size(); } // constraint index from 1 to num_constraint - unsigned constraint_slack(unsigned ci) const { return m_constraints[ci].m_slack; } - // unsat constraint stack - bool m_is_unsat; - unsigned_vector m_unsat_stack; // store all the unsat constraints - unsigned_vector m_index_in_unsat_stack; // which position is a constraint in the unsat_stack - - // configuration changed decreasing variables (score>0 and conf_change==true) - bool_var_vector m_goodvar_stack; - - - // information about solution - unsigned m_best_unsat; - double m_best_unsat_rate; - double m_last_best_unsat_rate; - int m_objective_value; // the objective function value corresponds to the current solution - bool_vector m_best_solution; // !var: the best solution so far - int m_best_objective_value = -1; // the objective value corresponds to the best solution so far - // for non-known instance, set as maximal - int m_best_known_value = INT_MAX; // best known value for this instance - - unsigned m_max_steps = (1 << 30); - - // dynamic noise - double m_noise = 9800; // normalized by 10000 - double m_noise_delta = 0.05; - - reslimit m_limit; - random_gen m_rand; - parallel* m_par; - model m_model; - void init(); void reinit(); void reinit_orig(); void init_cur_solution(); void init_slack(); void init_scores(); - void init_goodvars(); - - bool_var pick_var_gsat(); - - void flip_gsat(bool_var v); - + void init_goodvars(); + void pick_flip_lookahead(); void pick_flip_walksat(); - void flip_walksat(bool_var v); - bool propagate(literal lit); - void add_propagation(literal lit); - void walksat(); - - void gsat(); - void unsat(unsigned c); - void sat(unsigned c); - - bool tie_breaker_sat(bool_var v1, bool_var v2); - - bool tie_breaker_ccd(bool_var v1, bool_var v2); - void set_parameters(); - - void calculate_and_update_ob(); - - bool all_objectives_are_met() const; - void verify_solution() const; - void verify_unsat_stack() const; - void verify_constraint(constraint const& c) const; - + void verify_slack(constraint const& c) const; + void verify_slack() const; + bool verify_goodvar() const; unsigned constraint_value(constraint const& c) const; - unsigned constraint_coeff(constraint const& c, literal l) const; - void print_info(std::ostream& out); - void extract_model(); - - bool check_goodvar(); - void add_clause(unsigned sz, literal const* c); - - void add_unit(literal lit); - + void add_unit(literal lit, literal explain); std::ostream& display(std::ostream& out) const; - std::ostream& display(std::ostream& out, constraint const& c) const; - std::ostream& display(std::ostream& out, unsigned v, var_info const& vi) const; public: @@ -287,8 +235,6 @@ namespace sat { ~local_search(); - void add_soft(bool_var v, int weight); - void add_cardinality(unsigned sz, literal const* c, unsigned k); void add_pb(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k); @@ -307,8 +253,14 @@ namespace sat { void set_phase(bool_var v, lbool f); + void set_bias(bool_var v, lbool f); + bool get_phase(bool_var v) const { return is_true(v); } + inline bool cur_solution(bool_var v) const { return m_vars[v].m_value; } + + double break_count(bool_var v) const { return m_vars[v].m_slow_break; } + model& get_model() { return m_model; } void collect_statistics(statistics& st) const; diff --git a/src/sat/sat_lookahead.cpp b/src/sat/sat_lookahead.cpp index 389cdb19b..74ac831d8 100644 --- a/src/sat/sat_lookahead.cpp +++ b/src/sat/sat_lookahead.cpp @@ -92,7 +92,7 @@ namespace sat { m_binary[(~l2).index()].push_back(l1); m_binary_trail.push_back((~l1).index()); ++m_stats.m_add_binary; - if (m_s.m_config.m_drat) validate_binary(l1, l2); + if (m_s.m_config.m_drat && m_search_mode == lookahead_mode::searching) validate_binary(l1, l2); } void lookahead::del_binary(unsigned idx) { @@ -110,13 +110,11 @@ namespace sat { void lookahead::validate_binary(literal l1, literal l2) { - if (m_search_mode == lookahead_mode::searching) { - m_assumptions.push_back(l1); - m_assumptions.push_back(l2); - m_drat.add(m_assumptions); - m_assumptions.pop_back(); - m_assumptions.pop_back(); - } + m_assumptions.push_back(l1); + m_assumptions.push_back(l2); + m_s.m_drat.add(m_assumptions); + m_assumptions.pop_back(); + m_assumptions.pop_back(); } void lookahead::inc_bstamp() { @@ -323,9 +321,9 @@ namespace sat { double sum = 0; unsigned skip_candidates = 0; bool autarky = get_config().m_lookahead_global_autarky; - for (bool_var x : m_freevars) { - SASSERT(is_undef(x)); - if (!m_select_lookahead_vars.empty()) { + if (!m_select_lookahead_vars.empty()) { + for (bool_var x : m_freevars) { + SASSERT(is_undef(x)); if (m_select_lookahead_vars.contains(x)) { if (!autarky || newbies || in_reduced_clause(x)) { m_candidates.push_back(candidate(x, m_rating[x])); @@ -336,10 +334,15 @@ namespace sat { } } } - else if (newbies || active_prefix(x)) { - m_candidates.push_back(candidate(x, m_rating[x])); - sum += m_rating[x]; - } + } + if (m_candidates.empty() && (m_select_lookahead_vars.empty() || newbies)) { + for (bool_var x : m_freevars) { + SASSERT(is_undef(x)); + if (newbies || active_prefix(x)) { + m_candidates.push_back(candidate(x, m_rating[x])); + sum += m_rating[x]; + } + } } TRACE("sat", display_candidates(tout << "sum: " << sum << "\n");); if (skip_candidates > 0) { @@ -997,6 +1000,7 @@ namespace sat { void lookahead::init(bool learned) { m_delta_trigger = 0.0; m_delta_decrease = 0.0; + m_delta_fraction = m_s.m_config.m_lookahead_delta_fraction; m_config.m_dl_success = 0.8; m_inconsistent = false; m_qhead = 0; @@ -1031,7 +1035,7 @@ namespace sat { for (unsigned i = 0; i < trail_sz; ++i) { literal l = m_s.m_trail[i]; if (!m_s.was_eliminated(l.var())) { - if (m_s.m_config.m_drat) m_drat.add(l, false); + if (m_s.m_config.m_drat) m_s.m_drat.add(l, false); assign(l); } } @@ -1064,7 +1068,7 @@ namespace sat { case 3: add_ternary(c[0],c[1],c[2]); break; default: if (!learned) add_clause(c); break; } - if (m_s.m_config.m_drat) m_drat.add(c, false); + // if (m_s.m_config.m_drat) m_s.m_drat.add(c, false); } } @@ -1808,13 +1812,13 @@ namespace sat { unsigned lookahead::do_double(literal l, unsigned& base) { unsigned num_units = 0; - if (!inconsistent() && dl_enabled(l)) { + if (!inconsistent() && dl_enabled(l) && get_config().m_lookahead_double) { if (get_lookahead_reward(l) > m_delta_trigger) { if (dl_no_overflow(base)) { ++m_stats.m_double_lookahead_rounds; num_units = double_look(l, base); if (!inconsistent()) { - m_delta_trigger = get_lookahead_reward(l); + m_delta_trigger = m_delta_fraction*get_lookahead_reward(l); dl_disable(l); } } @@ -1845,13 +1849,15 @@ namespace sat { unsigned num_iterations = 0; while (change && num_iterations < m_config.m_dl_max_iterations && !inconsistent()) { num_iterations++; - for (unsigned i = 0; !inconsistent() && i < m_lookahead.size(); ++i) { - literal lit = m_lookahead[i].m_lit; + for (auto const& lh : m_lookahead) { + if (inconsistent()) break; + + literal lit = lh.m_lit; if (lit == last_changed) { SASSERT(change == false); break; } - unsigned level = base + m_lookahead[i].m_offset; + unsigned level = base + lh.m_offset; if (level + m_lookahead.size() >= dl_truth) { change = false; break; @@ -1869,6 +1875,7 @@ namespace sat { ++m_stats.m_double_lookahead_propagations; SASSERT(m_level == dl_truth); lookahead_backtrack(); + if (m_s.m_config.m_drat) validate_binary(~l, ~lit); assign(~lit); propagate(); change = true; @@ -1926,7 +1933,7 @@ namespace sat { void lookahead::validate_assign(literal l) { if (m_s.m_config.m_drat && m_search_mode == lookahead_mode::searching) { m_assumptions.push_back(l); - m_drat.add(m_assumptions); + m_s.m_drat.add(m_assumptions); m_assumptions.pop_back(); } } @@ -2006,6 +2013,7 @@ namespace sat { } bool lookahead::backtrack(literal_vector& trail, svector & is_decision) { + m_cube_state.m_backtracks++; while (inconsistent()) { if (trail.empty()) return false; if (is_decision.back()) { @@ -2026,6 +2034,7 @@ namespace sat { void lookahead::update_cube_statistics(statistics& st) { st.update("lh cube cutoffs", m_cube_state.m_cutoffs); st.update("lh cube conflicts", m_cube_state.m_conflicts); + st.update("lh cube backtracks", m_cube_state.m_backtracks); } double lookahead::psat_heur() { @@ -2098,7 +2107,9 @@ namespace sat { m_cube_state.m_freevars_threshold = m_freevars.size(); m_cube_state.m_psat_threshold = m_config.m_cube_cutoff == adaptive_psat_cutoff ? psat_heur() : dbl_max; // MN. only compute PSAT if enabled m_cube_state.inc_conflict(); - if (!backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision)) return l_false; + if (!backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision)) { + return l_false; + } continue; } pick_up_work: @@ -2131,7 +2142,9 @@ namespace sat { m_cube_state.m_freevars_threshold = prev_nfreevars; m_cube_state.m_psat_threshold = prev_psat; m_cube_state.inc_conflict(); - if (!backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision)) return l_false; + if (!backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision)) { + return l_false; + } continue; } if (lit == null_literal) { @@ -2160,7 +2173,7 @@ namespace sat { if (is_undef(lit)) { val = l_undef; } - if (is_true(lit)) { + else if (is_true(lit)) { val = l_true; } else { @@ -2279,7 +2292,7 @@ namespace sat { for (unsigned i = 0; i < m_trail.size() && !m_s.inconsistent(); ++i) { literal lit = m_trail[i]; if (m_s.value(lit) == l_undef && !m_s.was_eliminated(lit.var())) { - m_s.assign(lit, justification()); + m_s.assign_scoped(lit); ++num_units; } } @@ -2457,7 +2470,7 @@ namespace sat { for (unsigned i = 0; i < m_watches.size(); ++i) { watch_list const& wl = m_watches[i]; if (!wl.empty()) { - sat::display_watch_list(out << to_literal(i) << " -> ", dummy_allocator, wl); + sat::display_watch_list(out << to_literal(i) << " -> ", dummy_allocator, wl, nullptr); out << "\n"; } } diff --git a/src/sat/sat_lookahead.h b/src/sat/sat_lookahead.h index 046750832..f6103ee7c 100644 --- a/src/sat/sat_lookahead.h +++ b/src/sat/sat_lookahead.h @@ -181,6 +181,7 @@ namespace sat { double m_psat_threshold; unsigned m_conflicts; unsigned m_cutoffs; + unsigned m_backtracks; cube_state() { reset(); } void reset() { m_first = true; @@ -190,7 +191,7 @@ namespace sat { m_psat_threshold = dbl_max; reset_stats(); } - void reset_stats() { m_conflicts = 0; m_cutoffs = 0; } + void reset_stats() { m_conflicts = 0; m_cutoffs = 0; m_backtracks = 0;} void inc_conflict() { ++m_conflicts; } void inc_cutoff() { ++m_cutoffs; } }; @@ -198,8 +199,8 @@ namespace sat { config m_config; double m_delta_trigger; double m_delta_decrease; + double m_delta_fraction; - drat m_drat; literal_vector m_assumptions; literal_vector m_trail; // trail of units @@ -564,7 +565,6 @@ namespace sat { lookahead(solver& s) : m_s(s), m_num_vars(s.num_vars()), - m_drat(s), m_num_tc1(0), m_level(2), m_last_prefix_length(0), diff --git a/src/sat/sat_model_converter.cpp b/src/sat/sat_model_converter.cpp index d132f1cd4..577588305 100644 --- a/src/sat/sat_model_converter.cpp +++ b/src/sat/sat_model_converter.cpp @@ -95,7 +95,7 @@ namespace sat { IF_VERBOSE(0, display(verbose_stream() << "violated ate\n", *it) << "\n"); IF_VERBOSE(0, for (unsigned v = 0; v < m.size(); ++v) verbose_stream() << v << " := " << m[v] << "\n";); IF_VERBOSE(0, display(verbose_stream())); - exit(0); + UNREACHABLE(); first = false; } if (!sat && it->get_kind() != ATE && v0 != null_bool_var) { @@ -264,7 +264,7 @@ namespace sat { } void model_converter::insert(entry & e, literal_vector const& c) { - SASSERT(c.contains(literal(e.var(), false)) || c.contains(literal(e.var(), true))); + SASSERT(e.var() == null_bool_var || c.contains(literal(e.var(), false)) || c.contains(literal(e.var(), true))); SASSERT(m_entries.begin() <= &e); SASSERT(&e < m_entries.end()); for (literal l : c) e.m_clauses.push_back(l); diff --git a/src/sat/sat_model_converter.h b/src/sat/sat_model_converter.h index 65e132729..2ca340e5d 100644 --- a/src/sat/sat_model_converter.h +++ b/src/sat/sat_model_converter.h @@ -119,6 +119,7 @@ namespace sat { void add_ate(clause const& c); bool empty() const { return m_entries.empty(); } + unsigned size() const { return m_entries.size(); } void reset(); bool check_invariant(unsigned num_vars) const; diff --git a/src/sat/sat_parallel.cpp b/src/sat/sat_parallel.cpp index ce2080f8c..77a5f973d 100644 --- a/src/sat/sat_parallel.cpp +++ b/src/sat/sat_parallel.cpp @@ -113,7 +113,7 @@ namespace sat { s.m_params.set_sym("phase", symbol("random")); } m_solvers[i] = alloc(sat::solver, s.m_params, m_limits[i]); - m_solvers[i]->copy(s); + m_solvers[i]->copy(s, true); m_solvers[i]->set_par(this, i); push_child(m_solvers[i]->rlimit()); } @@ -235,7 +235,7 @@ namespace sat { // there could be multiple local search engines running at the same time. IF_VERBOSE(1, verbose_stream() << "(sat-parallel refresh :from " << m_num_clauses << " :to " << s.m_clauses.size() << ")\n";); m_solver_copy = alloc(solver, s.m_params, s.rlimit()); - m_solver_copy->copy(s); + m_solver_copy->copy(s, true); m_num_clauses = s.m_clauses.size(); } } @@ -292,7 +292,7 @@ namespace sat { { m_consumer_ready = true; if (m_solver_copy && s.m_clauses.size() > m_solver_copy->m_clauses.size()) { - s.copy(*m_solver_copy); + s.copy(*m_solver_copy, true); copied = true; m_num_clauses = s.m_clauses.size(); } diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index acde7e30c..88b196d04 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -39,6 +39,7 @@ 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.binary', BOOL, False, 'use Binary DRAT output format'), ('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, True, 'use cardinality solver'), @@ -71,9 +72,11 @@ def_module_params('sat', ('lookahead.cube.psat.trigger', DOUBLE, 5, 'trigger value to create lookahead cubes for PSAT cutoff. Used when lookahead.cube.cutoff is psat'), ('lookahead.preselect', BOOL, False, 'use pre-selection of subset of variables for branching'), ('lookahead_simplify', BOOL, False, 'use lookahead solver during simplification'), + ('lookahead.double', BOOL, True, 'enable doubld lookahead'), ('lookahead.use_learned', BOOL, False, 'use learned clauses when selecting lookahead literal'), ('lookahead_simplify.bca', BOOL, True, 'add learned binary clauses as part of lookahead simplification'), ('lookahead.global_autarky', BOOL, False, 'prefer to branch on variables that occur in clauses that are reduced'), + ('lookahead.delta_fraction', DOUBLE, 1.0, 'number between 0 and 1, the smaller the more literals are selected for double lookahead'), ('lookahead.reward', SYMBOL, 'march_cu', 'select lookahead heuristic: ternary, heule_schur (Heule Schur), heuleu (Heule Unit), unit, or march_cu')) # reward function used to determine which literal to cube on. # - ternary: reward function useful for random 3-SAT instances. Used by Heule and Knuth in March. diff --git a/src/sat/sat_probing.cpp b/src/sat/sat_probing.cpp index 52ab9f9f7..5689112d4 100644 --- a/src/sat/sat_probing.cpp +++ b/src/sat/sat_probing.cpp @@ -65,7 +65,11 @@ namespace sat { if (implied_lits) { for (literal lit : *implied_lits) { if (m_assigned.contains(lit)) { - s.assign(lit, justification()); + if (s.m_config.m_drat) { + s.m_drat.add(l, lit, true); + s.m_drat.add(~l, lit, true); + } + s.assign_scoped(lit); m_num_assigned++; } } @@ -73,14 +77,14 @@ namespace sat { else { m_to_assert.reset(); s.push(); - s.assign(l, justification()); + s.assign_scoped(l); m_counter--; unsigned old_tr_sz = s.m_trail.size(); s.propagate(false); if (s.inconsistent()) { // ~l must be true s.pop(1); - s.assign(~l, justification()); + s.assign_scoped(~l); s.propagate(false); return false; } @@ -95,8 +99,12 @@ namespace sat { cache_bins(l, old_tr_sz); s.pop(1); - for (literal l : m_to_assert) { - s.assign(l, justification()); + for (literal lit : m_to_assert) { + if (s.m_config.m_drat) { + s.m_drat.add(l, lit, true); + s.m_drat.add(~l, lit, true); + } + s.assign_scoped(lit); m_num_assigned++; } } @@ -111,13 +119,13 @@ namespace sat { m_counter--; s.push(); literal l(v, false); - s.assign(l, justification()); + s.assign_scoped(l); unsigned old_tr_sz = s.m_trail.size(); s.propagate(false); if (s.inconsistent()) { // ~l must be true s.pop(1); - s.assign(~l, justification()); + s.assign_scoped(~l); s.propagate(false); m_num_assigned++; return; @@ -168,7 +176,7 @@ namespace sat { struct probing::report { probing & m_probing; stopwatch m_watch; - unsigned m_num_assigned; + unsigned m_num_assigned; report(probing & p): m_probing(p), m_num_assigned(p.m_num_assigned) { @@ -177,12 +185,13 @@ namespace sat { ~report() { m_watch.stop(); - IF_VERBOSE(SAT_VB_LVL, - verbose_stream() << " (sat-probing :probing-assigned " - << (m_probing.m_num_assigned - m_num_assigned) - << " :cost " << m_probing.m_counter; + unsigned units = (m_probing.m_num_assigned - m_num_assigned); + IF_VERBOSE(2, + verbose_stream() << " (sat-probing"; + if (units > 0) verbose_stream() << " :probing-assigned " << units; + verbose_stream() << " :cost " << m_probing.m_counter; if (m_probing.m_stopped_at != 0) verbose_stream() << " :stopped-at " << m_probing.m_stopped_at; - verbose_stream() << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); + verbose_stream() << mem_stat() << m_watch << ")\n";); } }; @@ -259,7 +268,7 @@ namespace sat { } void probing::collect_statistics(statistics & st) const { - st.update("probing assigned", m_num_assigned); + st.update("sat probing assigned", m_num_assigned); } void probing::reset_statistics() { diff --git a/src/sat/sat_scc.cpp b/src/sat/sat_scc.cpp index e430bcb47..7ce073975 100644 --- a/src/sat/sat_scc.cpp +++ b/src/sat/sat_scc.cpp @@ -47,20 +47,23 @@ namespace sat { stopwatch m_watch; unsigned m_num_elim; unsigned m_num_elim_bin; + unsigned m_trail_size; report(scc & c): m_scc(c), m_num_elim(c.m_num_elim), - m_num_elim_bin(c.m_num_elim_bin) { + m_num_elim_bin(c.m_num_elim_bin), + m_trail_size(c.m_solver.init_trail_size()) { m_watch.start(); } ~report() { m_watch.stop(); unsigned elim_bin = m_scc.m_num_elim_bin - m_num_elim_bin; - IF_VERBOSE(SAT_VB_LVL, + unsigned num_units = m_scc.m_solver.init_trail_size() - m_trail_size; + IF_VERBOSE(2, verbose_stream() << " (sat-scc :elim-vars " << (m_scc.m_num_elim - m_num_elim); if (elim_bin > 0) verbose_stream() << " :elim-bin " << elim_bin; - verbose_stream() << mk_stat(m_scc.m_solver) - << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); + if (num_units > 0) verbose_stream() << " :units " << num_units; + verbose_stream() << m_watch << ")\n";); } }; @@ -178,7 +181,7 @@ namespace sat { l2_idx = s[j]; j--; if (to_literal(l2_idx) == ~l) { - m_solver.set_conflict(justification()); + m_solver.set_conflict(); return 0; } if (m_solver.is_external(to_literal(l2_idx).var())) { @@ -244,15 +247,15 @@ namespace sat { } void scc::reduce_tr() { - unsigned quota = 0, num_reduced = 0; - while ((num_reduced = reduce_tr(false)) > quota) { quota = std::max(100u, num_reduced / 2); } - quota = 0; - while ((num_reduced = reduce_tr(true)) > quota) { quota = std::max(100u, num_reduced / 2); } + unsigned quota = 0, num_reduced = 0, count = 0; + while ((num_reduced = reduce_tr(false)) > quota && count++ < 10) { quota = std::max(100u, num_reduced / 2); } + quota = 0; count = 0; + while ((num_reduced = reduce_tr(true)) > quota && count++ < 10) { quota = std::max(100u, num_reduced / 2); } } void scc::collect_statistics(statistics & st) const { - st.update("elim bool vars scc", m_num_elim); - st.update("elim binary", m_num_elim_bin); + st.update("sat scc elim vars", m_num_elim); + st.update("sat scc elim binary", m_num_elim_bin); } void scc::reset_statistics() { diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 21d264af5..58dab0ff5 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -124,23 +124,20 @@ namespace sat { } } - inline void simplifier::remove_clause_core(clause & c) { - for (literal l : c) - insert_elim_todo(l.var()); - m_sub_todo.erase(c); - c.set_removed(true); - TRACE("resolution_bug", tout << "del_clause: " << c << "\n";); - m_need_cleanup = true; - } - inline void simplifier::remove_clause(clause & c) { - remove_clause_core(c); - m_use_list.erase(c); - } - - inline void simplifier::remove_clause(clause & c, literal l) { - remove_clause_core(c); - m_use_list.erase(c, l); + if (!c.was_removed()) { + if (s.m_config.m_drat) { + s.m_drat.del(c); + } + for (literal l : c) { + insert_elim_todo(l.var()); + } + m_sub_todo.erase(c); + c.set_removed(true); + TRACE("resolution_bug", tout << "del_clause: " << c << "\n";); + m_need_cleanup = true; + m_use_list.erase(c); + } } inline void simplifier::set_learned(clause & c) { @@ -247,8 +244,8 @@ namespace sat { cleanup_watches(); move_clauses(s.m_learned, true); move_clauses(s.m_clauses, false); - cleanup_clauses(s.m_learned, true, vars_eliminated, m_learned_in_use_lists); - cleanup_clauses(s.m_clauses, false, vars_eliminated, true); + cleanup_clauses(s.m_learned, true, vars_eliminated); + cleanup_clauses(s.m_clauses, false, vars_eliminated); } CASSERT("sat_solver", s.check_invariant()); @@ -304,7 +301,7 @@ namespace sat { cs.set_end(it2); } - void simplifier::cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists) { + void simplifier::cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated) { TRACE("sat", tout << "cleanup_clauses\n";); clause_vector::iterator it = cs.begin(); clause_vector::iterator it2 = it; @@ -330,35 +327,43 @@ namespace sat { } } - if (cleanup_clause(c, in_use_lists)) { + unsigned sz0 = c.size(); + if (cleanup_clause(c)) { s.del_clause(c); continue; } unsigned sz = c.size(); - if (sz == 0) { - s.set_conflict(justification()); + switch(sz) { + case 0: + s.set_conflict(); for (; it != end; ++it, ++it2) { *it2 = *it; } + cs.set_end(it2); + return; + case 1: + s.assign_unit(c[0]); + c.restore(sz0); + s.del_clause(c); break; - } - if (sz == 1) { - s.assign(c[0], justification()); - s.del_clause(c); - continue; - } - if (sz == 2) { + case 2: s.mk_bin_clause(c[0], c[1], c.is_learned()); + c.restore(sz0); s.del_clause(c); - continue; - } - *it2 = *it; - it2++; - if (!c.frozen()) { - s.attach_clause(c); - if (s.m_config.m_drat) { + break; + default: + if (s.m_config.m_drat && sz0 != sz) { s.m_drat.add(c, true); + c.restore(sz0); + s.m_drat.del(c); + c.shrink(sz); } + *it2 = *it; + it2++; + if (!c.frozen()) { + s.attach_clause(c); + } + break; } } cs.set_end(it2); @@ -468,8 +473,9 @@ namespace sat { clause & c2 = *(*it); if (!c2.was_removed() && *l_it == null_literal) { // c2 was subsumed - if (c1.is_learned() && !c2.is_learned()) + if (c1.is_learned() && !c2.is_learned()) { s.set_learned(c1, false); + } TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";); remove_clause(c2); m_num_subsumed++; @@ -581,7 +587,7 @@ namespace sat { Return true if the clause is satisfied */ - bool simplifier::cleanup_clause(clause & c, bool in_use_list) { + bool simplifier::cleanup_clause(clause & c) { bool r = false; unsigned sz = c.size(); unsigned j = 0; @@ -596,11 +602,6 @@ namespace sat { break; case l_false: m_need_cleanup = true; - if (in_use_list && !c.frozen()) { - // Remark: if in_use_list is false, then the given clause was not added to the use lists. - // Remark: frozen clauses are not added to the use lists. - m_use_list.get(l).erase_not_removed(c); - } break; case l_true: r = true; @@ -611,10 +612,8 @@ namespace sat { break; } } - if (j < sz) { - if (s.m_config.m_drat) s.m_drat.del(c); + if (j < sz && !r) { c.shrink(j); - if (s.m_config.m_drat) s.m_drat.add(c, true); } return r; } @@ -648,7 +647,7 @@ namespace sat { inline void simplifier::propagate_unit(literal l) { unsigned old_trail_sz = s.m_trail.size(); - s.assign(l, justification()); + s.assign_scoped(l); s.propagate_core(false); // must not use propagate(), since s.m_clauses is not in a consistent state. if (s.inconsistent()) return; @@ -663,7 +662,7 @@ namespace sat { for (auto it = cs.mk_iterator(); !it.at_end(); ) { clause & c = it.curr(); it.next(); - remove_clause(c, l); + remove_clause(c); } cs.reset(); } @@ -674,39 +673,55 @@ namespace sat { m_need_cleanup = true; m_num_elim_lits++; insert_elim_todo(l.var()); - c.elim(l); - if (s.m_config.m_drat) s.m_drat.add(c, true); - // if (s.m_config.m_drat) s.m_drat.del(c0); // can delete previous version + if (s.m_config.m_drat && c.contains(l)) { + m_dummy.set(c.size(), c.begin(), c.is_learned()); + c.elim(l); + s.m_drat.add(c, true); + s.m_drat.del(*m_dummy.get()); + } + else { + c.elim(l); + } clause_use_list & occurs = m_use_list.get(l); occurs.erase_not_removed(c); m_sub_counter -= occurs.size()/2; - if (cleanup_clause(c, true /* clause is in the use lists */)) { + + unsigned sz0 = c.size(); + if (cleanup_clause(c)) { // clause was satisfied TRACE("elim_lit", tout << "clause was satisfied\n";); remove_clause(c); return; } - switch (c.size()) { + unsigned sz = c.size(); + switch (sz) { case 0: TRACE("elim_lit", tout << "clause is empty\n";); - s.set_conflict(justification()); - return; + s.set_conflict(); + break; case 1: TRACE("elim_lit", tout << "clause became unit: " << c[0] << "\n";); + c.restore(sz0); propagate_unit(c[0]); - // propagate_unit will delete c. - // remove_clause(c); - return; + // unit propagation removes c + break; case 2: TRACE("elim_lit", tout << "clause became binary: " << c[0] << " " << c[1] << "\n";); + c.restore(sz0); s.mk_bin_clause(c[0], c[1], c.is_learned()); - m_sub_bin_todo.push_back(bin_clause(c[0], c[1], c.is_learned())); + m_sub_bin_todo.push_back(bin_clause(c[0], c[1], c.is_learned())); remove_clause(c); - return; + break; default: + if (s.m_config.m_drat && sz0 != sz) { + s.m_drat.add(c, true); + c.restore(sz0); + s.m_drat.del(c); + c.shrink(sz); + } TRACE("elim_lit", tout << "result: " << c << "\n";); m_sub_todo.insert(c); - return; + break; } } @@ -871,27 +886,36 @@ namespace sat { m_sub_counter--; TRACE("subsumption", tout << "next: " << c << "\n";); if (s.m_trail.size() > m_last_sub_trail_sz) { - if (cleanup_clause(c, true /* clause is in the use_lists */)) { + unsigned sz0 = c.size(); + if (cleanup_clause(c)) { remove_clause(c); continue; } unsigned sz = c.size(); - if (sz == 0) { - s.set_conflict(justification()); + switch (sz) { + case 0: + s.set_conflict(); return; - } - if (sz == 1) { + case 1: + c.restore(sz0); propagate_unit(c[0]); - // propagate_unit will delete c. - // remove_clause(c); + // unit propagation removes c continue; - } - if (sz == 2) { + case 2: TRACE("subsumption", tout << "clause became binary: " << c << "\n";); s.mk_bin_clause(c[0], c[1], c.is_learned()); m_sub_bin_todo.push_back(bin_clause(c[0], c[1], c.is_learned())); + c.restore(sz0); remove_clause(c); continue; + default: + if (s.m_config.m_drat && sz != sz0) { + s.m_drat.add(c, true); + c.restore(sz0); + s.m_drat.del(c); + c.shrink(sz); + } + break; } } TRACE("subsumption", tout << "using: " << c << "\n";); @@ -991,6 +1015,9 @@ namespace sat { svector m_in_intersection; unsigned m_ala_qhead; clause_wrapper m_clause; + unsigned m_ala_cost; + unsigned m_ala_benefit; + unsigned m_ala_max_cost; blocked_clause_elim(simplifier & _s, unsigned limit, model_converter & _mc, use_list & l, vector & wlist): @@ -998,12 +1025,15 @@ namespace sat { m_counter(limit), m_mc(_mc), m_queue(l, wlist), - m_clause(null_literal, null_literal) { + m_clause(null_literal, null_literal), + m_ala_cost(0), + m_ala_benefit(0) { m_in_intersection.resize(s.s.num_vars() * 2, false); + m_ala_max_cost = (s.s.m_clauses.size() * s.m_num_calls)/5; } void insert(literal l) { - VERIFY(process_var(l.var())); + SASSERT(process_var(l.var())); m_queue.insert(l); } @@ -1011,6 +1041,10 @@ namespace sat { return !s.s.is_assumption(v) && !s.was_eliminated(v) && !s.is_external(v) && s.value(v) == l_undef; } + bool reached_max_cost() { + return m_ala_benefit <= m_ala_cost * 100 && m_ala_cost > m_ala_max_cost; + } + enum elim_type { bce_t, cce_t, @@ -1042,6 +1076,7 @@ namespace sat { } void insert_queue() { + m_queue.reset(); unsigned num_vars = s.s.num_vars(); for (bool_var v = 0; v < num_vars; v++) { if (process_var(v)) { @@ -1130,7 +1165,6 @@ namespace sat { if (m_intersection.empty() && !first) { m_tautology.shrink(tsz); } - // if (first) IF_VERBOSE(0, verbose_stream() << "taut: " << m_tautology << "\n";); return first; } @@ -1188,9 +1222,6 @@ namespace sat { RI literals. */ void minimize_covered_clause(unsigned idx) { - // IF_VERBOSE(0, verbose_stream() << "minimize: " << m_covered_clause - // << " @ " << idx << "\n" << "tautology: " << m_tautology << "\n";); - literal _blocked = m_covered_clause[idx]; for (literal l : m_tautology) VERIFY(s.is_marked(l)); for (literal l : m_covered_clause) s.unmark_visited(l); for (literal l : m_tautology) s.mark_visited(l); @@ -1200,14 +1231,6 @@ namespace sat { if (m_covered_antecedent[i] == clause_ante()) s.mark_visited(lit); if (s.is_marked(lit)) idx = i; } - if (false && _blocked.var() == 8074) { - IF_VERBOSE(0, verbose_stream() << "covered: " << m_covered_clause << "\n"; - verbose_stream() << "tautology: " << m_tautology << "\n"; - verbose_stream() << "index: " << idx << "\n"; - for (unsigned i = idx; i > 0; --i) { - m_covered_antecedent[i].display(verbose_stream(), m_covered_clause[i]); - }); - } for (unsigned i = idx; i > 0; --i) { literal lit = m_covered_clause[i]; //s.mark_visited(lit); @@ -1251,10 +1274,6 @@ namespace sat { // unsigned sz0 = m_covered_clause.size(); m_covered_clause.resize(j); VERIFY(j >= m_clause.size()); - if (false && _blocked.var() == 16774) { - IF_VERBOSE(0, verbose_stream() << "covered: " << m_covered_clause << "\n"); - } - // IF_VERBOSE(0, verbose_stream() << "reduced from size " << sz0 << " to " << m_covered_clause << "\n" << m_clause << "\n";); } /* @@ -1272,17 +1291,19 @@ namespace sat { * unless C contains lit, and it is a tautology. */ bool add_ala() { - for (; m_ala_qhead < m_covered_clause.size(); ++m_ala_qhead) { + unsigned init_size = m_covered_clause.size(); + for (; m_ala_qhead < m_covered_clause.size() && m_ala_qhead < 5*init_size && !reached_max_cost(); ++m_ala_qhead) { + ++m_ala_cost; literal l = m_covered_clause[m_ala_qhead]; for (watched & w : s.get_wlist(~l)) { if (w.is_binary_non_learned_clause()) { literal lit = w.get_literal(); if (revisit_binary(l, lit)) continue; if (s.is_marked(lit)) { + ++m_ala_benefit; return true; } if (!s.is_marked(~lit)) { - // if (m_covered_clause[0].var() == 10219) IF_VERBOSE(0, verbose_stream() << "ala: " << l << " " << lit << "\n"); m_covered_clause.push_back(~lit); m_covered_antecedent.push_back(clause_ante(l, false)); s.mark_visited(~lit); @@ -1310,9 +1331,9 @@ namespace sat { } if (!ok) continue; if (lit1 == null_literal) { + ++m_ala_benefit; return true; } - // if (m_covered_clause[0].var() == 10219) IF_VERBOSE(0, verbose_stream() << "ala: " << c << " " << lit1 << "\n"); m_covered_clause.push_back(~lit1); m_covered_antecedent.push_back(clause_ante(c)); s.mark_visited(~lit1); @@ -1349,7 +1370,6 @@ namespace sat { } bool above_threshold(unsigned sz0) const { - // if (sz0 * 400 < m_covered_clause.size()) IF_VERBOSE(0, verbose_stream() << "above threshold " << sz0 << " " << m_covered_clause.size() << "\n";); return sz0 * 400 < m_covered_clause.size(); } @@ -1420,19 +1440,6 @@ namespace sat { if (check_abce_tautology(m_covered_clause[i])) { blocked = m_covered_clause[i]; reset_mark(); -#if 0 - if (sz0 == 3 && blocked.var() == 10219) { - IF_VERBOSE(0, verbose_stream() << "abce: " << m_covered_clause << "\n"; - for (literal l : m_covered_clause) verbose_stream() << s.value(l) << "\n"; - ); - literal l = blocked; - 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_VERBOSE(0, verbose_stream() << c << "\n"); - } - } -#endif m_covered_clause.shrink(sz0); if (et == bce_t) return bce_t; k = model_converter::ABCE; @@ -1495,7 +1502,9 @@ namespace sat { template void cce_binary() { - while (!m_queue.empty() && m_counter >= 0) { + m_ala_cost = 0; + m_ala_benefit = 0; + while (!m_queue.empty() && m_counter >= 0 && !reached_max_cost()) { s.checkpoint(); process_cce_binary(m_queue.next()); } @@ -1507,7 +1516,7 @@ namespace sat { watch_list & wlist = s.get_wlist(~l); m_counter -= wlist.size(); model_converter::kind k; - for (watched & w : wlist) { + for (watched& w : wlist) { if (!w.is_binary_non_learned_clause()) continue; if (!select_clause(2)) continue; literal l2 = w.get_literal(); @@ -1527,15 +1536,20 @@ namespace sat { block_covered_binary(w, l, blocked, k); break; } + s.checkpoint(); } } template void cce_clauses() { literal blocked; + m_ala_cost = 0; + m_ala_benefit = 0; model_converter::kind k; - for (clause* cp : s.s.m_clauses) { - clause& c = *cp; + unsigned start = s.s.m_rand(); + unsigned sz = s.s.m_clauses.size(); + for (unsigned i = 0; i < sz; ++i) { + clause& c = *s.s.m_clauses[(i + start) % sz]; if (c.was_removed() || c.is_learned()) continue; if (!select_clause(c.size())) continue; elim_type r = cce(c, blocked, k); @@ -1552,7 +1566,11 @@ namespace sat { s.set_learned(c); break; } - } + s.checkpoint(); + if (reached_max_cost()) { + break; + } + } } void inc_bc(elim_type et) { @@ -1574,15 +1592,6 @@ namespace sat { } void block_covered_clause(clause& c, literal l, model_converter::kind k) { - if (false) { - IF_VERBOSE(0, verbose_stream() << "blocked: " << l << " @ " << c << " :covered " << m_covered_clause << "\n"; - s.m_use_list.display(verbose_stream() << "use " << l << ":", l); - s.m_use_list.display(verbose_stream() << "use " << ~l << ":", ~l); - s.s.display_watch_list(verbose_stream() << ~l << ": ", s.get_wlist(l)) << "\n"; - s.s.display_watch_list(verbose_stream() << l << ": ", s.get_wlist(~l)) << "\n"; - ); - - } TRACE("blocked_clause", tout << "new blocked clause: " << c << "\n";); SASSERT(!s.is_external(l)); model_converter::entry& new_entry = m_mc.mk(k, l.var()); @@ -1841,36 +1850,7 @@ namespace sat { } void simplifier::add_non_learned_binary_clause(literal l1, literal l2) { -#if 0 - if ((l1.var() == 2039 || l2.var() == 2039) && - (l1.var() == 27042 || l2.var() == 27042)) { - IF_VERBOSE(1, verbose_stream() << "add_bin: " << l1 << " " << l2 << "\n"); - } -#endif -#if 0 - watched* w; - watch_list & wlist1 = get_wlist(~l1); - watch_list & wlist2 = get_wlist(~l2); - w = find_binary_watch(wlist1, l2); - if (w) { - if (w->is_learned()) - w->set_learned(false); - } - else { - wlist1.push_back(watched(l2, false)); - } - - w = find_binary_watch(wlist2, l1); - if (w) { - if (w->is_learned()) - w->set_learned(false); - } - else { - wlist2.push_back(watched(l1, false)); - } -#else s.mk_bin_clause(l1, l2, false); -#endif } /** @@ -1881,17 +1861,13 @@ namespace sat { for (auto & w : wlist) { if (w.is_binary_clause()) { literal l2 = w.get_literal(); + // m_drat.del(l, l2); watch_list & wlist2 = get_wlist(~l2); watch_list::iterator it2 = wlist2.begin(); watch_list::iterator itprev = it2; watch_list::iterator end2 = wlist2.end(); for (; it2 != end2; ++it2) { if (it2->is_binary_clause() && it2->get_literal() == l) { - if ((l.var() == 2039 || l2.var() == 2039) && - (l.var() == 27042 || l2.var() == 27042)) { - IF_VERBOSE(1, verbose_stream() << "remove_bin: " << l << " " << l2 << "\n"); - } - TRACE("bin_clause_bug", tout << "removing: " << l << " " << it2->get_literal() << "\n";); m_sub_bin_todo.erase(bin_clause(l2, l, it2->is_learned())); continue; @@ -1915,11 +1891,16 @@ namespace sat { clause & c = it.curr(); it.next(); SASSERT(c.contains(l)); - c.set_removed(true); - m_use_list.erase(c, l); - m_sub_todo.erase(c); - TRACE("resolution_bug", tout << "del_clause (elim_var): " << c << "\n";); - m_need_cleanup = true; + if (!c.was_removed()) { + if (s.m_config.m_drat) { + s.m_drat.del(c); + } + c.set_removed(true); + m_use_list.erase(c, l); + m_sub_todo.erase(c); + TRACE("resolution_bug", tout << "del_clause (elim_var): " << c << "\n";); + m_need_cleanup = true; + } } } @@ -1992,41 +1973,27 @@ namespace sat { m_elim_counter -= num_pos * num_neg + before_lits; - if (false) { - literal l(v, false); - IF_VERBOSE(0, - verbose_stream() << "elim: " << l << "\n"; - s.display_watch_list(verbose_stream() << ~l << ": ", get_wlist(l)) << "\n"; - s.display_watch_list(verbose_stream() << l << ": ", get_wlist(~l)) << "\n";); - } // eliminate variable ++s.m_stats.m_elim_var_res; VERIFY(!is_external(v)); model_converter::entry & mc_entry = s.m_mc.mk(model_converter::ELIM_VAR, v); save_clauses(mc_entry, m_pos_cls); save_clauses(mc_entry, m_neg_cls); - s.m_eliminated[v] = true; - remove_bin_clauses(pos_l); - remove_bin_clauses(neg_l); - remove_clauses(pos_occs, pos_l); - remove_clauses(neg_occs, neg_l); - pos_occs.reset(); - neg_occs.reset(); - + s.m_eliminated[v] = true; m_elim_counter -= num_pos * num_neg + before_lits; for (auto & c1 : m_pos_cls) { for (auto & c2 : m_neg_cls) { m_new_cls.reset(); if (!resolve(c1, c2, pos_l, m_new_cls)) - continue; - if (false && v == 767) IF_VERBOSE(0, verbose_stream() << "elim: " << c1 << " + " << c2 << " -> " << m_new_cls << "\n"); + continue; TRACE("resolution_new_cls", tout << c1 << "\n" << c2 << "\n-->\n" << m_new_cls << "\n";); - if (cleanup_clause(m_new_cls)) + if (cleanup_clause(m_new_cls)) { continue; // clause is already satisfied. + } switch (m_new_cls.size()) { case 0: - s.set_conflict(justification()); + s.set_conflict(); break; case 1: propagate_unit(m_new_cls[0]); @@ -2057,6 +2024,12 @@ namespace sat { return true; } } + remove_bin_clauses(pos_l); + remove_bin_clauses(neg_l); + remove_clauses(pos_occs, pos_l); + remove_clauses(neg_occs, neg_l); + pos_occs.reset(); + neg_occs.reset(); return true; } @@ -2142,15 +2115,15 @@ namespace sat { } void simplifier::collect_statistics(statistics & st) const { - st.update("subsumed", m_num_subsumed); - st.update("subsumption resolution", m_num_sub_res); - st.update("elim literals", m_num_elim_lits); - st.update("bce", m_num_bce); - st.update("cce", m_num_cce); - st.update("acce", m_num_acce); - st.update("abce", m_num_abce); - st.update("bca", m_num_bca); - st.update("ate", m_num_ate); + st.update("sat subsumed", m_num_subsumed); + st.update("sat subs resolution", m_num_sub_res); + st.update("sat elim literals", m_num_elim_lits); + st.update("sat bce", m_num_bce); + st.update("sat cce", m_num_cce); + st.update("sat acce", m_num_acce); + st.update("sat abce", m_num_abce); + st.update("sat bca", m_num_bca); + st.update("sat ate", m_num_ate); } void simplifier::reset_statistics() { diff --git a/src/sat/sat_simplifier.h b/src/sat/sat_simplifier.h index 990b87b10..2aa2b4252 100644 --- a/src/sat/sat_simplifier.h +++ b/src/sat/sat_simplifier.h @@ -133,9 +133,7 @@ namespace sat { void register_clauses(clause_vector & cs); - void remove_clause_core(clause & c); void remove_clause(clause & c); - void remove_clause(clause & c, literal l); void set_learned(clause & c); void set_learned(literal l1, literal l2); @@ -154,7 +152,7 @@ namespace sat { void collect_subsumed0(clause const & c1, clause_vector & out); void back_subsumption0(clause & c1); - bool cleanup_clause(clause & c, bool in_use_list); + bool cleanup_clause(clause & c); bool cleanup_clause(literal_vector & c); void elim_lit(clause & c, literal l); void elim_dup_bins(); @@ -164,7 +162,7 @@ namespace sat { void cleanup_watches(); void move_clauses(clause_vector & cs, bool learned); - void cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists); + void cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated); bool is_external(bool_var v) const; bool is_external(literal l) const { return is_external(l.var()); } diff --git a/src/sat/sat_simplifier_params.pyg b/src/sat/sat_simplifier_params.pyg index 3757aad2d..60ca4a49b 100644 --- a/src/sat/sat_simplifier_params.pyg +++ b/src/sat/sat_simplifier_params.pyg @@ -2,7 +2,7 @@ def_module_params(module_name='sat', class_name='sat_simplifier_params', export=True, params=(('bce', BOOL, False, 'eliminate blocked clauses'), - ('abce', BOOL, False, 'eliminate blocked clauses using asymmmetric literals'), + ('abce', BOOL, False, 'eliminate blocked clauses using asymmetric literals'), ('cce', BOOL, False, 'eliminate covered clauses'), ('ate', BOOL, True, 'asymmetric tautology elimination'), ('acce', BOOL, False, 'eliminate covered clauses using asymmetric added literals'), @@ -11,7 +11,7 @@ def_module_params(module_name='sat', ('bce_delay', UINT, 2, 'delay eliminate blocked clauses until simplification round'), ('retain_blocked_clauses', BOOL, True, 'retain blocked clauses as lemmas'), ('blocked_clause_limit', UINT, 100000000, 'maximum number of literals visited during blocked clause elimination'), - ('override_incremental', BOOL, False, 'override incemental safety gaps. Enable elimination of blocked clauses and variables even if solver is reused'), + ('override_incremental', BOOL, False, 'override incremental safety gaps. Enable elimination of blocked clauses and variables even if solver is reused'), ('resolution.limit', UINT, 500000000, 'approx. maximum number of literals visited during variable elimination'), ('resolution.occ_cutoff', UINT, 10, 'first cutoff (on number of positive/negative occurrences) for Boolean variable elimination'), ('resolution.occ_cutoff_range1', UINT, 8, 'second cutoff (number of positive/negative occurrences) for Boolean variable elimination, for problems containing less than res_cls_cutoff1 clauses'), diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index ee3bc7880..8b7d53897 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -27,6 +27,9 @@ Revision History: #include "util/trace.h" #include "util/max_cliques.h" #include "util/gparams.h" +#ifdef _MSC_VER +# include +#endif // define to update glue during propagation #define UPDATE_GLUE @@ -34,7 +37,7 @@ Revision History: namespace sat { solver::solver(params_ref const & p, reslimit& l): - m_rlimit(l), + solver_core(l), m_checkpoint_enabled(true), m_config(p), m_par(nullptr), @@ -95,7 +98,7 @@ namespace sat { if (ext) ext->set_solver(this); } - void solver::copy(solver const & src) { + void solver::copy(solver const & src, bool copy_learned) { pop_to_base_level(); del_clauses(m_clauses); del_clauses(m_learned); @@ -120,6 +123,11 @@ namespace sat { m_qhead = 0; m_trail.reset(); m_scopes.reset(); + + if (src.inconsistent()) { + set_conflict(); + return; + } // create new vars for (bool_var v = num_vars(); v < src.num_vars(); v++) { @@ -132,11 +140,9 @@ namespace sat { m_phase[v] = src.m_phase[v]; m_prev_phase[v] = src.m_prev_phase[v]; -#if 1 // inherit activity: m_activity[v] = src.m_activity[v]; m_case_split_queue.activity_changed_eh(v, false); -#endif } // @@ -149,7 +155,7 @@ namespace sat { unsigned trail_sz = src.init_trail_size(); for (unsigned i = 0; i < trail_sz; ++i) { - assign(src.m_trail[i], justification()); + assign_unit(src.m_trail[i]); } // copy binary clauses @@ -187,7 +193,7 @@ namespace sat { // copy high quality lemmas unsigned num_learned = 0; for (clause* c : src.m_learned) { - if (c->glue() <= 2 || (c->size() <= 40 && c->glue() <= 8)) { + if (c->glue() <= 2 || (c->size() <= 40 && c->glue() <= 8) || copy_learned) { buffer.reset(); for (literal l : *c) buffer.push_back(l); clause* c1 = mk_clause_core(buffer.size(), buffer.c_ptr(), true); @@ -198,7 +204,7 @@ namespace sat { } } } - IF_VERBOSE(1, verbose_stream() << "(sat.copy :learned " << num_learned << ")\n";); + IF_VERBOSE(2, verbose_stream() << "(sat.copy :learned " << num_learned << ")\n";); } m_user_scope_literals.reset(); @@ -270,7 +276,7 @@ namespace sat { } } - void solver::mk_clause(unsigned num_lits, literal * lits, bool learned) { + clause* solver::mk_clause(unsigned num_lits, literal * lits, bool learned) { m_model_is_current = false; DEBUG_CODE({ for (unsigned i = 0; i < num_lits; i++) @@ -278,24 +284,24 @@ namespace sat { }); if (m_user_scope_literals.empty()) { - mk_clause_core(num_lits, lits, learned); + return mk_clause_core(num_lits, lits, learned); } else { m_aux_literals.reset(); m_aux_literals.append(num_lits, lits); m_aux_literals.append(m_user_scope_literals); - mk_clause_core(m_aux_literals.size(), m_aux_literals.c_ptr(), learned); + return mk_clause_core(m_aux_literals.size(), m_aux_literals.c_ptr(), learned); } } - void solver::mk_clause(literal l1, literal l2, bool learned) { + clause* solver::mk_clause(literal l1, literal l2, bool learned) { literal ls[2] = { l1, l2 }; - mk_clause(2, ls, learned); + return mk_clause(2, ls, learned); } - void solver::mk_clause(literal l1, literal l2, literal l3, bool learned) { + clause* solver::mk_clause(literal l1, literal l2, literal l3, bool learned) { literal ls[3] = { l1, l2, l3 }; - mk_clause(3, ls, learned); + return mk_clause(3, ls, learned); } void solver::del_clause(clause& c) { @@ -305,31 +311,38 @@ namespace sat { if (c.frozen()) { --m_num_frozen; } - if (m_config.m_drat && !m_drat.is_cleaned(c)) { + if (!c.was_removed() && m_config.m_drat && !m_drat.is_cleaned(c)) { m_drat.del(c); } - dealloc_clause(&c); - m_stats.m_del_clause++; + dealloc_clause(&c); + if (m_searching) + m_stats.m_del_clause++; } clause * solver::mk_clause_core(unsigned num_lits, literal * lits, bool learned) { TRACE("sat", tout << "mk_clause: " << mk_lits_pp(num_lits, lits) << (learned?" learned":" aux") << "\n";); if (!learned) { + unsigned old_sz = num_lits; bool keep = simplify_clause(num_lits, lits); TRACE("sat_mk_clause", tout << "mk_clause (after simp), keep: " << keep << "\n" << mk_lits_pp(num_lits, lits) << "\n";); if (!keep) { return nullptr; // clause is equivalent to true. } + // if an input clause is simplified, then log the simplified version as learned + if (!learned && old_sz > num_lits && m_config.m_drat) { + m_lemma.reset(); + m_lemma.append(num_lits, lits); + m_drat.add(m_lemma); + } ++m_stats.m_non_learned_generation; } switch (num_lits) { case 0: - if (m_config.m_drat) m_drat.add(); - set_conflict(justification()); + set_conflict(); return nullptr; case 1: - assign(lits[0], justification()); + assign_unit(lits[0]); return nullptr; case 2: mk_bin_clause(lits[0], lits[1], learned); @@ -343,12 +356,12 @@ namespace sat { } void solver::mk_bin_clause(literal l1, literal l2, bool learned) { - if (find_binary_watch(get_wlist(~l1), ~l2)) { - assign(l1, justification()); + if (find_binary_watch(get_wlist(~l1), ~l2) && value(l1) == l_undef) { + assign_core(l1, 0, justification(l2)); return; } - if (find_binary_watch(get_wlist(~l2), ~l1)) { - assign(l2, justification()); + if (find_binary_watch(get_wlist(~l2), ~l1) && value(l2) == l_undef) { + assign_core(l2, 0, justification(l1)); return; } watched* w0 = find_binary_watch(get_wlist(~l1), l2); @@ -534,7 +547,7 @@ namespace sat { void solver::defrag_clauses() { if (memory_pressure()) return; pop(scope_lvl()); - IF_VERBOSE(1, verbose_stream() << "(sat-defrag)\n"); + IF_VERBOSE(2, verbose_stream() << "(sat-defrag)\n"); clause_allocator& alloc = m_cls_allocator[!m_cls_allocator_idx]; ptr_vector new_clauses, new_learned; for (clause* c : m_clauses) c->unmark_used(); @@ -690,7 +703,7 @@ namespace sat { for (; i < num_lits; i++) { literal curr = lits[i]; lbool val = value(curr); - if (!lvl0 && m_level[curr.var()] > 0) + if (!lvl0 && lvl(curr) > 0) val = l_undef; switch (val) { case l_false: @@ -701,7 +714,7 @@ namespace sat { if (curr != prev) { prev = curr; if (i != j) - lits[j] = lits[i]; + std::swap(lits[j], lits[i]); j++; } break; @@ -759,17 +772,20 @@ namespace sat { m_not_l = not_l; } - void solver::assign_core(literal l, unsigned lvl, justification j) { + void solver::assign_core(literal l, unsigned _lvl, justification j) { SASSERT(value(l) == l_undef); - TRACE("sat_assign_core", tout << l << " " << j << " level: " << lvl << "\n";); + TRACE("sat_assign_core", tout << l << " " << j << " level: " << _lvl << "\n";); + if (_lvl == 0 && m_config.m_drat) { + m_drat.add(l, !j.is_none()); + } + if (at_base_lvl()) { - if (m_config.m_drat) m_drat.add(l, !j.is_none()); j = justification(); // erase justification for level 0 } m_assignment[l.index()] = l_true; m_assignment[(~l).index()] = l_false; bool_var v = l.var(); - m_level[v] = lvl; + m_level[v] = scope_lvl(); m_justification[v] = j; m_phase[v] = static_cast(l.sign()); m_assigned_since_gc[v] = true; @@ -1023,7 +1039,7 @@ namespace sat { m_cuber = nullptr; if (is_first) { pop_to_base_level(); - set_conflict(justification()); + set_conflict(); } break; case l_true: { @@ -1058,7 +1074,7 @@ namespace sat { init_reason_unknown(); pop_to_base_level(); m_stats.m_units = init_trail_size(); - IF_VERBOSE(2, verbose_stream() << "(sat.sat-solver)\n";); + IF_VERBOSE(2, verbose_stream() << "(sat.solver)\n";); SASSERT(at_base_lvl()); if (m_config.m_local_search) { @@ -1144,6 +1160,7 @@ namespace sat { } catch (const abort_solver &) { m_reason_unknown = "sat.giveup"; + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort giveup\")\n";); return l_undef; } } @@ -1158,7 +1175,6 @@ namespace sat { SASSERT(!m_local_search); m_local_search = alloc(local_search); local_search& srch = *m_local_search; - srch.config().set_config(m_config); srch.import(*this, false); scoped_rl.push_child(&srch.rlimit()); lbool r = srch.check(num_lits, lits, nullptr); @@ -1183,9 +1199,8 @@ namespace sat { int num_threads = num_extra_solvers + 1 + num_local_search + num_unit_walk; for (int i = 0; i < num_local_search; ++i) { local_search* l = alloc(local_search); - l->config().set_config(m_config); - l->config().set_random_seed(m_config.m_random_seed + i); l->import(*this, false); + l->config().set_random_seed(m_config.m_random_seed + i); ls.push_back(l); } @@ -1337,7 +1352,7 @@ namespace sat { SASSERT(lit.var() < m_par_num_vars); if (lvl(lit.var()) != 0 || value(lit) != l_true) { ++num_in; - assign(lit, justification()); + assign_unit(lit); } } if (num_in > 0 || num_out > 0) { @@ -1423,8 +1438,8 @@ namespace sat { SASSERT(phase != l_undef); literal next_lit(next, phase == l_false); - assign(next_lit, justification()); TRACE("sat_decide", tout << scope_lvl() << ": next-case-split: " << next_lit << "\n";); + assign_scoped(next_lit); return true; } @@ -1451,14 +1466,13 @@ namespace sat { lbool solver::propagate_and_backjump_step(bool& done) { done = true; propagate(true); - if (!inconsistent()) - return l_true; + if (!inconsistent()) { + return should_restart() ? l_undef : l_true; + } if (!resolve_conflict()) return l_false; if (reached_max_conflicts()) return l_undef; - if (should_restart()) - return l_undef; if (at_base_lvl()) { cleanup(false); // cleaner may propagate frozen clauses if (inconsistent()) { @@ -1526,14 +1540,14 @@ namespace sat { for (unsigned i = 0; !inconsistent() && i < m_user_scope_literals.size(); ++i) { literal nlit = ~m_user_scope_literals[i]; - assign(nlit, justification()); + assign_scoped(nlit); } for (unsigned i = 0; !inconsistent() && i < num_lits; ++i) { literal lit = lits[i]; SASSERT(is_external(lit.var())); add_assumption(lit); - assign(lit, justification()); + assign_scoped(lit); } m_search_lvl = scope_lvl(); SASSERT(m_search_lvl == 1); @@ -1572,7 +1586,7 @@ namespace sat { for (literal lit : m_min_core) { SASSERT(is_external(lit.var())); add_assumption(lit); - assign(lit, justification()); + assign_scoped(lit); } propagate(false); SASSERT(inconsistent()); @@ -1585,11 +1599,11 @@ namespace sat { push(); for (literal lit : m_user_scope_literals) { if (inconsistent()) break; - assign(~lit, justification()); + assign_scoped(~lit); } for (literal lit : m_assumptions) { if (inconsistent()) break; - assign(lit, justification()); + assign_scoped(lit); } TRACE("sat", for (literal a : m_assumptions) { @@ -1609,6 +1623,12 @@ namespace sat { return tracking_assumptions() && m_assumption_set.contains(l); } + void solver::set_activity(bool_var v, unsigned act) { + unsigned old_act = m_activity[v]; + m_activity[v] = act; + m_case_split_queue.activity_changed_eh(v, act > old_act); + } + bool solver::is_assumption(bool_var v) const { return is_assumption(literal(v, false)) || is_assumption(literal(v, true)); } @@ -1623,6 +1643,8 @@ namespace sat { m_gc_threshold = m_config.m_gc_initial; m_defrag_threshold = 2; m_restarts = 0; + m_last_position_log = 0; + m_restart_logs = 0; m_simplifications = 0; m_conflicts_since_init = 0; m_next_simplify = m_config.m_simplify_delay; @@ -1638,6 +1660,7 @@ namespace sat { m_min_core.reset(); m_simplifier.init_search(); TRACE("sat", display(tout);); + } /** @@ -1647,6 +1670,7 @@ namespace sat { if (m_conflicts_since_init < m_next_simplify) { return; } + log_stats(); m_simplifications++; IF_VERBOSE(2, verbose_stream() << "(sat.simplify :simplifications " << m_simplifications << ")\n";); @@ -1691,8 +1715,6 @@ namespace sat { lh.collect_statistics(m_aux_stats); } - TRACE("sat", display(tout << "consistent: " << (!inconsistent()) << "\n");); - reinit_assumptions(); if (m_next_simplify == 0) { @@ -1704,7 +1726,6 @@ namespace sat { m_next_simplify = m_conflicts_since_init + m_config.m_simplify_max; } - if (m_par) m_par->set_phase(*this); #if 0 @@ -1762,39 +1783,31 @@ namespace sat { } #endif - IF_VERBOSE(10, verbose_stream() << "\"checking model\"\n";); - if (!check_clauses(m_model)) { - 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)) { - IF_VERBOSE(0, verbose_stream() << "failure checking clauses on transformed model\n";); - IF_VERBOSE(10, m_mc.display(verbose_stream())); - //IF_VERBOSE(0, display_units(verbose_stream())); - //IF_VERBOSE(0, display(verbose_stream())); - IF_VERBOSE(0, for (bool_var v = 0; v < num; v++) verbose_stream() << v << ": " << m_model[v] << "\n";); - - 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)) { - //IF_VERBOSE(0, display(verbose_stream())); - //IF_VERBOSE(0, display_watches(verbose_stream())); - IF_VERBOSE(0, m_mc.display(verbose_stream())); - IF_VERBOSE(0, display_units(verbose_stream())); - //IF_VERBOSE(0, m_clone->display(verbose_stream() << "clone\n")); - throw solver_exception("check model failed (for cloned solver)"); + IF_VERBOSE(10, verbose_stream() << "\"checking model\"\n";); + if (!check_clauses(m_model)) { + throw solver_exception("check model failed"); } } + + if (m_config.m_drat) { + m_drat.check_model(m_model); + } + + m_mc(m_model); + + if (m_clone && !check_clauses(m_model)) { + IF_VERBOSE(1, verbose_stream() << "failure checking clauses on transformed model\n";); + IF_VERBOSE(10, m_mc.display(verbose_stream())); + IF_VERBOSE(10, display_model(verbose_stream())); + throw solver_exception("check model failed"); + } + + if (m_clone && !m_clone->check_model(m_model)) { + IF_VERBOSE(1, m_mc.display(verbose_stream())); + IF_VERBOSE(1, display_units(verbose_stream())); + throw solver_exception("check model failed (for cloned solver)"); + } } bool solver::check_clauses(model const& m) const { @@ -1802,7 +1815,7 @@ namespace sat { for (clause const* cp : m_clauses) { clause const & c = *cp; if (!c.satisfied_by(m)) { - IF_VERBOSE(0, verbose_stream() << "failed clause " << c.id() << ": " << c << "\n";); + IF_VERBOSE(1, verbose_stream() << "failed clause " << c.id() << ": " << c << "\n";); TRACE("sat", tout << "failed: " << c << "\n"; tout << "assumptions: " << m_assumptions << "\n"; tout << "trail: " << m_trail << "\n"; @@ -1810,7 +1823,7 @@ namespace sat { m_mc.display(tout); ); for (literal l : c) { - if (was_eliminated(l.var())) IF_VERBOSE(0, verbose_stream() << "eliminated: " << l << "\n";); + if (was_eliminated(l.var())) IF_VERBOSE(1, verbose_stream() << "eliminated: " << l << "\n";); } ok = false; } @@ -1826,8 +1839,8 @@ namespace sat { if (l.index() > l2.index()) continue; if (value_at(l2, m) != l_true) { - IF_VERBOSE(0, verbose_stream() << "failed binary: " << l << " := " << value_at(l, m) << " " << l2 << " := " << value_at(l2, m) << "\n"); - IF_VERBOSE(0, verbose_stream() << "elim l1: " << was_eliminated(l.var()) << " elim l2: " << was_eliminated(l2) << "\n"); + IF_VERBOSE(1, verbose_stream() << "failed binary: " << l << " := " << value_at(l, m) << " " << l2 << " := " << value_at(l2, m) << "\n"); + IF_VERBOSE(1, verbose_stream() << "elim l1: " << was_eliminated(l.var()) << " elim l2: " << was_eliminated(l2) << "\n"); TRACE("sat", m_mc.display(tout << "failed binary: " << l << " " << l2 << "\n");); ok = false; } @@ -1838,7 +1851,7 @@ namespace sat { for (literal l : m_assumptions) { if (value_at(l, m) != l_true) { VERIFY(is_external(l.var())); - IF_VERBOSE(0, verbose_stream() << "assumption: " << l << " does not model check " << value_at(l, m) << "\n";); + IF_VERBOSE(1, verbose_stream() << "assumption: " << l << " does not model check " << value_at(l, m) << "\n";); TRACE("sat", tout << l << " does not model check\n"; tout << "trail: " << m_trail << "\n"; @@ -1859,8 +1872,8 @@ namespace sat { if (ok && !m_mc.check_model(m)) { ok = false; TRACE("sat", tout << "model: " << m << "\n"; m_mc.display(tout);); + IF_VERBOSE(0, verbose_stream() << "model check failed\n"); } - IF_VERBOSE(1, verbose_stream() << "model check " << (ok?"OK":"failed") << "\n";); return ok; } @@ -1873,17 +1886,84 @@ namespace sat { m_config.m_restart_margin * m_slow_glue_avg <= m_fast_glue_avg; } + void solver::log_stats() { + m_restart_logs++; + + std::stringstream strm; + strm << "(sat.stats " << std::setw(6) << m_stats.m_conflict << " " + << std::setw(6) << m_stats.m_decision << " " + << std::setw(4) << m_stats.m_restart + << mk_stat(*this) + << " " << std::setw(6) << std::setprecision(2) << m_stopwatch.get_current_seconds() << ")\n"; + std::string str(strm.str()); + svector nums; + for (size_t i = 0; i < str.size(); ++i) { + while (i < str.size() && str[i] != ' ') ++i; + while (i < str.size() && str[i] == ' ') ++i; + // position of first character after space + if (i < str.size()) { + nums.push_back(i); + } + } + bool same = m_last_positions.size() == nums.size(); + size_t diff = 0; + for (unsigned i = 0; i < nums.size() && same; ++i) { + if (m_last_positions[i] > nums[i]) diff += m_last_positions[i] - nums[i]; + if (m_last_positions[i] < nums[i]) diff += nums[i] - m_last_positions[i]; + } + if (m_last_positions.empty() || + m_restart_logs >= 20 + m_last_position_log || + (m_restart_logs >= 6 + m_last_position_log && (!same || diff > 3))) { + m_last_position_log = m_restart_logs; + // conflicts restarts learned gc time + // decisions clauses units memory + int adjust[9] = { -3, -3, -3, -1, -3, -2, -1, -2, -1 }; + char const* tag[9] = { ":conflicts ", ":decisions ", ":restarts ", ":clauses/bin ", ":learned/bin ", ":units ", ":gc ", ":memory ", ":time" }; + std::stringstream l1, l2; + l1 << "(sat.stats "; + l2 << "(sat.stats "; + size_t p1 = 11, p2 = 11; + SASSERT(nums.size() == 9); + for (unsigned i = 0; i < 9 && i < nums.size(); ++i) { + size_t p = nums[i]; + if (i & 0x1) { + // odd positions + for (; p2 < p + adjust[i]; ++p2) l2 << " "; + p2 += strlen(tag[i]); + l2 << tag[i]; + } + else { + // even positions + for (; p1 < p + adjust[i]; ++p1) l1 << " "; + p1 += strlen(tag[i]); + l1 << tag[i]; + } + } + for (; p1 + 2 < str.size(); ++p1) l1 << " "; + for (; p2 + 2 < str.size(); ++p2) l2 << " "; + l1 << ")\n"; + l2 << ")\n"; + IF_VERBOSE(1, verbose_stream() << l1.str() << l2.str()); + m_last_positions.reset(); + m_last_positions.append(nums); + } + IF_VERBOSE(1, verbose_stream() << str); + } + void solver::restart(bool to_base) { m_stats.m_restart++; m_restarts++; - if (m_conflicts_since_init > m_restart_next_out + 500) { - m_restart_next_out = m_conflicts_since_init; - IF_VERBOSE(1, - verbose_stream() << "(sat-restart :conflicts " << m_stats.m_conflict << " :decisions " << m_stats.m_decision - << " :restarts " << m_stats.m_restart << mk_stat(*this) - << " :time " << std::fixed << std::setprecision(2) << m_stopwatch.get_current_seconds() << ")\n";); + if (m_conflicts_since_init >= m_restart_next_out && get_verbosity_level() >= 1) { + if (0 == m_restart_next_out) { + m_restart_next_out = 1; + } + else { + m_restart_next_out = std::min(m_conflicts_since_init + 50000, (3*m_restart_next_out)/2 + 1); + } + log_stats(); } IF_VERBOSE(30, display_status(verbose_stream());); + TRACE("sat", tout << "restart " << restart_level(to_base) << "\n";); pop_reinit(restart_level(to_base)); set_next_restart(); } @@ -2206,19 +2286,24 @@ namespace sat { unsigned new_sz = j; switch (new_sz) { case 0: - set_conflict(justification()); + if (m_config.m_drat) m_drat.add(); + set_conflict(); return false; case 1: - assign(c[0], justification()); + assign_unit(c[0]); return false; case 2: mk_bin_clause(c[0], c[1], true); return false; default: if (new_sz != sz) { - if (m_config.m_drat) m_drat.del(c); c.shrink(new_sz); - if (m_config.m_drat) m_drat.add(c, true); + if (m_config.m_drat) { + m_drat.add(c, true); + c.restore(sz); + m_drat.del(c); + c.shrink(new_sz); + } } attach_clause(c); return true; @@ -2262,7 +2347,6 @@ namespace sat { } bool solver::resolve_conflict_core() { - m_conflicts_since_init++; m_conflicts_since_restart++; m_conflicts_since_gc++; @@ -2272,7 +2356,7 @@ namespace sat { } m_conflict_lvl = get_max_lvl(m_not_l, m_conflict); - TRACE("sat", tout << "conflict detected at level " << m_conflict_lvl << " for "; + TRACE("sat_verbose", tout << "conflict detected at level " << m_conflict_lvl << " for "; if (m_not_l == literal()) tout << "null literal\n"; else tout << m_not_l << "\n";); @@ -2409,15 +2493,15 @@ namespace sat { unsigned glue = num_diff_levels(m_lemma.size(), m_lemma.c_ptr()); m_fast_glue_avg.update(glue); m_slow_glue_avg.update(glue); - pop_reinit(m_scope_lvl - new_scope_lvl); - TRACE("sat_conflict_detail", tout << new_scope_lvl << "\n"; display(tout);); + TRACE("sat_conflict_detail", tout << glue << " " << new_scope_lvl << "\n";); // unsound: m_asymm_branch.minimize(m_scc, m_lemma); clause * lemma = mk_clause_core(m_lemma.size(), m_lemma.c_ptr(), true); if (lemma) { lemma->set_glue(glue); if (m_par) m_par->share_clause(*this, *lemma); } + TRACE("sat_conflict_detail", tout << new_scope_lvl << "\n";); decay_activity(); updt_phase_counters(); } @@ -2487,8 +2571,8 @@ namespace sat { TRACE("sat", display(tout); unsigned level = 0; for (literal l : m_trail) { - if (level != m_level[l.var()]) { - level = m_level[l.var()]; + if (level != lvl(l)) { + level = lvl(l); tout << level << ": "; } tout << l; @@ -2498,6 +2582,7 @@ namespace sat { tout << " "; } tout << "\n"; + tout << "conflict level: " << m_conflict_lvl << "\n"; ); m_core.reset(); @@ -2564,7 +2649,7 @@ namespace sat { if (m_config.m_core_minimize) { if (m_min_core_valid && m_min_core.size() < m_core.size()) { - IF_VERBOSE(1, verbose_stream() << "(sat.updating core " << m_min_core.size() << " " << m_core.size() << ")\n";); + IF_VERBOSE(2, verbose_stream() << "(sat.updating core " << m_min_core.size() << " " << m_core.size() << ")\n";); m_core.reset(); m_core.append(m_min_core); } @@ -2665,7 +2750,7 @@ namespace sat { for (unsigned i = head; i < sz; i++) { literal l = m_trail[i]; bool_var v = l.var(); - TRACE("forget_phase", tout << "forgeting phase of l: " << l << "\n";); + TRACE("forget_phase", tout << "forgetting phase of l: " << l << "\n";); m_phase[v] = PHASE_NOT_AVAILABLE; } } @@ -2879,20 +2964,15 @@ namespace sat { void solver::minimize_lemma() { SASSERT(!m_lemma.empty()); SASSERT(m_unmark.empty()); - //m_unmark.reset(); updt_lemma_lvl_set(); unsigned sz = m_lemma.size(); unsigned i = 1; // the first literal is the FUIP unsigned j = 1; - //bool drop = false; - //unsigned bound = sz/5+10; for (; i < sz; i++) { literal l = m_lemma[i]; if (implied_by_marked(l)) { - TRACE("sat", tout << "drop: " << l << "\n";); m_unmark.push_back(l.var()); - //drop = true; } else { if (j != i) { @@ -2900,12 +2980,6 @@ namespace sat { } j++; } -#if 0 - if (!drop && i >= bound) { - j = sz; - break; - } -#endif } reset_unmark(0); @@ -3254,7 +3328,7 @@ namespace sat { bool_var solver::max_var(bool learned, bool_var v) { m_user_bin_clauses.reset(); - collect_bin_clauses(m_user_bin_clauses, learned); + collect_bin_clauses(m_user_bin_clauses, learned, false); for (unsigned i = 0; i < m_user_bin_clauses.size(); ++i) { literal l1 = m_user_bin_clauses[i].first; literal l2 = m_user_bin_clauses[i].second; @@ -3465,6 +3539,14 @@ namespace sat { return true; } + std::ostream& solver::display_model(std::ostream& out) const { + unsigned num = num_vars(); + for (bool_var v = 0; v < num; v++) { + out << v << ": " << m_model[v] << "\n"; + } + return out; +} + void solver::display_binary(std::ostream & out) const { unsigned sz = m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; l_idx++) { @@ -3510,16 +3592,39 @@ namespace sat { } std::ostream& solver::display_justification(std::ostream & out, justification const& js) const { - out << js; - if (js.is_clause()) { - out << get_clause(js); + switch (js.get_kind()) { + case justification::NONE: + out << "none "; // << js.level(); + break; + case justification::BINARY: + out << "binary " << js.get_literal() << "@" << lvl(js.get_literal()); + break; + case justification::TERNARY: + out << "ternary " << js.get_literal1() << "@" << lvl(js.get_literal1()) << " "; + out << js.get_literal2() << "@" << lvl(js.get_literal2()); + break; + case justification::CLAUSE: { + out << "("; + bool first = true; + for (literal l : get_clause(js)) { + if (first) first = false; else out << " "; + out << l << "@" << lvl(l); + } + out << ")"; + break; } - else if (js.is_ext_justification() && m_ext) { - m_ext->display_justification(out << " ", js.get_ext_justification_idx()); + case justification::EXT_JUSTIFICATION: + if (m_ext) { + m_ext->display_justification(out << " ", js.get_ext_justification_idx()); + } + break; + default: + break; } return out; } + unsigned solver::num_clauses() const { unsigned num_cls = m_trail.size(); // units; unsigned l_idx = 0; @@ -3624,7 +3729,7 @@ namespace sat { } std::ostream& solver::display_watch_list(std::ostream& out, watch_list const& wl) const { - return sat::display_watch_list(out, cls_allocator(), wl); + return sat::display_watch_list(out, cls_allocator(), wl, m_ext.get()); } void solver::display_assignment(std::ostream & out) const { @@ -3730,7 +3835,7 @@ namespace sat { max_cliques mc; m_user_bin_clauses.reset(); m_binary_clause_graph.reset(); - collect_bin_clauses(m_user_bin_clauses, true); + collect_bin_clauses(m_user_bin_clauses, true, false); hashtable, default_eq > seen_bc; for (auto const& b : m_user_bin_clauses) { literal l1 = b.first; @@ -3914,7 +4019,7 @@ namespace sat { m_reason_unknown = "sat.max.conflicts"; IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = " << m_conflicts_since_init << "\")\n";); } - return true; + return !inconsistent(); } return false; } @@ -4033,7 +4138,7 @@ namespace sat { } push(); ++num_assigned; - assign(~lit, justification()); + assign_scoped(~lit); propagate(false); while (inconsistent()) { if (!resolve_conflict()) { @@ -4309,24 +4414,24 @@ namespace sat { } void stats::collect_statistics(statistics & st) const { - st.update("mk bool var", m_mk_var); - st.update("mk binary clause", m_mk_bin_clause); - st.update("mk ternary clause", m_mk_ter_clause); - st.update("mk clause", m_mk_clause); - st.update("gc clause", m_gc_clause); - st.update("del clause", m_del_clause); - st.update("conflicts", m_conflict); - st.update("propagations", m_propagate); - st.update("decisions", m_decision); - st.update("binary propagations", m_bin_propagate); - st.update("ternary propagations", m_ter_propagate); - st.update("restarts", m_restart); - st.update("minimized lits", m_minimized_lits); - st.update("dyn subsumption resolution", m_dyn_sub_res); - st.update("blocked correction sets", m_blocked_corr_sets); - st.update("units", m_units); - st.update("elim bool vars res", m_elim_var_res); - st.update("elim bool vars bdd", m_elim_var_bdd); + st.update("sat mk clause 2ary", m_mk_bin_clause); + st.update("sat mk clause 3ary", m_mk_ter_clause); + st.update("sat mk clause nary", m_mk_clause); + st.update("sat mk var", m_mk_var); + st.update("sat gc clause", m_gc_clause); + st.update("sat del clause", m_del_clause); + st.update("sat conflicts", m_conflict); + st.update("sat decisions", m_decision); + st.update("sat propagations 2ary", m_bin_propagate); + st.update("sat propagations 3ary", m_ter_propagate); + st.update("sat propagations nary", m_propagate); + st.update("sat restarts", m_restart); + st.update("sat minimized lits", m_minimized_lits); + st.update("sat subs resolution dyn", m_dyn_sub_res); + st.update("sat blocked correction sets", m_blocked_corr_sets); + st.update("sat units", m_units); + st.update("sat elim bool vars res", m_elim_var_res); + st.update("sat elim bool vars bdd", m_elim_var_bdd); } void stats::reset() { @@ -4336,16 +4441,11 @@ namespace sat { void mk_stat::display(std::ostream & out) const { unsigned given, learned; m_solver.num_binary(given, learned); - if (!m_solver.m_clauses.empty()) - out << " :clauses " << m_solver.m_clauses.size() + given << "/" << given; - if (!m_solver.m_learned.empty()) { - out << " :learned " << (m_solver.m_learned.size() + learned - m_solver.m_num_frozen) << "/" << learned; - if (m_solver.m_num_frozen > 0) - out << " :frozen " << m_solver.m_num_frozen; - } - out << " :units " << m_solver.init_trail_size(); - out << " :gc-clause " << m_solver.m_stats.m_gc_clause; - out << mem_stat(); + out << " " << std::setw(5) << m_solver.m_clauses.size() + given << "/" << given; + out << " " << std::setw(5) << (m_solver.m_learned.size() + learned - m_solver.m_num_frozen) << "/" << learned; + out << " " << std::setw(3) << m_solver.init_trail_size(); + out << " " << std::setw(7) << m_solver.m_stats.m_gc_clause << " "; + out << " " << std::setw(7) << mem_stat(); } std::ostream & operator<<(std::ostream & out, mk_stat const & stat) { diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 8402fc898..c000ccb79 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -37,6 +37,7 @@ Revision History: #include "sat/sat_drat.h" #include "sat/sat_parallel.h" #include "sat/sat_local_search.h" +#include "sat/sat_solver_core.h" #include "util/params.h" #include "util/statistics.h" #include "util/stopwatch.h" @@ -75,11 +76,10 @@ namespace sat { void collect_statistics(statistics & st) const; }; - class solver { + class solver : public solver_core { public: struct abort_solver {}; protected: - reslimit& m_rlimit; bool m_checkpoint_enabled; config m_config; stats m_stats; @@ -190,19 +190,19 @@ namespace sat { friend class scoped_detach; public: solver(params_ref const & p, reslimit& l); - ~solver(); + ~solver() override; // ----------------------- // // Misc // // ----------------------- - void updt_params(params_ref const & p); + void updt_params(params_ref const & p) override; static void collect_param_descrs(param_descrs & d); - void collect_statistics(statistics & st) const; + void collect_statistics(statistics & st) const override; void reset_statistics(); - void display_status(std::ostream & out) const; + void display_status(std::ostream & out) const override; /** \brief Copy (non learned) clauses from src to this solver. @@ -210,18 +210,23 @@ namespace sat { \pre the model converter of src and this must be empty */ - void copy(solver const & src); + void copy(solver const & src, bool copy_learned = false); // ----------------------- // // Variable & Clause creation // // ----------------------- + void add_clause(unsigned num_lits, literal * lits, bool learned) override { mk_clause(num_lits, lits, learned); } + bool_var add_var(bool ext) override { return mk_var(ext, true); } + bool_var mk_var(bool ext = false, bool dvar = true); - void mk_clause(literal_vector const& lits, bool learned = false) { mk_clause(lits.size(), lits.c_ptr(), learned); } - void mk_clause(unsigned num_lits, literal * lits, bool learned = false); - void mk_clause(literal l1, literal l2, bool learned = false); - void mk_clause(literal l1, literal l2, literal l3, bool learned = false); + clause* mk_clause(literal_vector const& lits, bool learned = false) { return mk_clause(lits.size(), lits.c_ptr(), learned); } + clause* mk_clause(unsigned num_lits, literal * lits, bool learned = false); + clause* mk_clause(literal l1, literal l2, bool learned = false); + clause* mk_clause(literal l1, literal l2, literal l3, bool learned = false); + + random_gen& rand() { return m_rand; } protected: inline clause_allocator& cls_allocator() { return m_cls_allocator[m_cls_allocator_idx]; } @@ -279,28 +284,28 @@ namespace sat { // // ----------------------- public: - bool inconsistent() const { return m_inconsistent; } - unsigned num_vars() const { return m_level.size(); } - unsigned num_clauses() const; + bool inconsistent() const override { return m_inconsistent; } + unsigned num_vars() const override { return m_level.size(); } + unsigned num_clauses() const override; void num_binary(unsigned& given, unsigned& learned) const; unsigned num_restarts() const { return m_restarts; } - bool is_external(bool_var v) const { return m_external[v] != 0; } - bool is_external(literal l) const { return is_external(l.var()); } - void set_external(bool_var v); - void set_non_external(bool_var v); + bool is_external(bool_var v) const override { return m_external[v] != 0; } + void set_external(bool_var v) override; + void set_non_external(bool_var v) override; bool was_eliminated(bool_var v) const { return m_eliminated[v] != 0; } - void set_eliminated(bool_var v, bool f) { m_eliminated[v] = f; } + void set_eliminated(bool_var v, bool f) override { m_eliminated[v] = f; } bool was_eliminated(literal l) const { return was_eliminated(l.var()); } unsigned scope_lvl() const { return m_scope_lvl; } unsigned search_lvl() const { return m_search_lvl; } bool at_search_lvl() const { return m_scope_lvl == m_search_lvl; } - bool at_base_lvl() const { return m_scope_lvl == 0; } + bool at_base_lvl() const override { return m_scope_lvl == 0; } lbool value(literal l) const { return static_cast(m_assignment[l.index()]); } lbool value(bool_var v) const { return static_cast(m_assignment[literal(v, false).index()]); } unsigned lvl(bool_var v) const { return m_level[v]; } unsigned lvl(literal l) const { return m_level[l.var()]; } - unsigned init_trail_size() const { return at_base_lvl() ? m_trail.size() : m_scopes[0].m_trail_lim; } - literal trail_literal(unsigned i) const { return m_trail[i]; } + unsigned init_trail_size() const override { return at_base_lvl() ? m_trail.size() : m_scopes[0].m_trail_lim; } + unsigned trail_size() const { return m_trail.size(); } + literal trail_literal(unsigned i) const override { return m_trail[i]; } literal scope_literal(unsigned n) const { return m_trail[m_scopes[n].m_trail_lim]; } void assign(literal l, justification j) { TRACE("sat_assign", tout << l << " previous value: " << value(l) << "\n";); @@ -311,8 +316,11 @@ namespace sat { } } void assign_core(literal l, unsigned lvl, justification jst); + void assign_unit(literal l) { assign(l, justification()); } + void assign_scoped(literal l) { assign(l, justification()); } void set_conflict(justification c, literal not_l); void set_conflict(justification c) { set_conflict(c, null_literal); } + void set_conflict() { set_conflict(justification()); } lbool status(clause const & c) const; clause_offset get_offset(clause const & c) const { return cls_allocator().get_offset(&c); } void checkpoint() { @@ -320,6 +328,7 @@ namespace sat { if (!m_rlimit.inc()) { m_mc.reset(); m_model_is_current = false; + TRACE("sat", tout << "canceled\n";); throw solver_exception(Z3_CANCELED_MSG); } ++m_num_checkpoints; @@ -332,8 +341,8 @@ namespace sat { config const& get_config() const { return m_config; } void set_incremental(bool b) { m_config.m_incremental = b; } bool is_incremental() const { return m_config.m_incremental; } - extension* get_extension() const { return m_ext.get(); } - void set_extension(extension* e); + extension* get_extension() const override { return m_ext.get(); } + void set_extension(extension* e) override; bool set_root(literal l, literal r); void flush_roots(); typedef std::pair bin_clause; @@ -368,17 +377,18 @@ namespace sat { // // ----------------------- public: - lbool check(unsigned num_lits = 0, literal const* lits = nullptr); + lbool check(unsigned num_lits = 0, literal const* lits = nullptr) override; - model const & get_model() const { return m_model; } + model const & get_model() const override { return m_model; } bool model_is_current() const { return m_model_is_current; } - literal_vector const& get_core() const { return m_core; } + literal_vector const& get_core() const override { return m_core; } model_converter const & get_model_converter() const { return m_mc; } - void flush(model_converter& mc) { mc.flush(m_mc); } + void flush(model_converter& mc) override { mc.flush(m_mc); } void set_model(model const& mdl); - char const* get_reason_unknown() const { return m_reason_unknown.c_str(); } + char const* get_reason_unknown() const override { return m_reason_unknown.c_str(); } bool check_clauses(model const& m) const; bool is_assumption(bool_var v) const; + void set_activity(bool_var v, unsigned act); lbool cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level); @@ -421,7 +431,11 @@ namespace sat { void mk_model(); bool check_model(model const & m) const; void restart(bool to_base); + svector m_last_positions; + unsigned m_last_position_log; + unsigned m_restart_logs; unsigned restart_level(bool to_base); + void log_stats(); bool should_restart() const; void set_next_restart(); bool reached_max_conflicts(); @@ -540,10 +554,10 @@ namespace sat { bool_var max_var(bool learned, bool_var v); public: - void user_push(); - void user_pop(unsigned num_scopes); - void pop_to_base_level(); - unsigned num_user_scopes() const { return m_user_scope_literals.size(); } + void user_push() override; + void user_pop(unsigned num_scopes) override; + void pop_to_base_level() override; + unsigned num_user_scopes() const override { return m_user_scope_literals.size(); } reslimit& rlimit() { return m_rlimit; } // ----------------------- // @@ -640,8 +654,9 @@ namespace sat { clause * const * begin_learned() const { return m_learned.begin(); } clause * const * end_learned() const { return m_learned.end(); } clause_vector const& learned() const { return m_learned; } - clause_vector const& clauses() const { return m_clauses; } - void collect_bin_clauses(svector & r, bool learned, bool learned_only = false) const; + clause_vector const& clauses() const override { return m_clauses; } + void collect_bin_clauses(svector & r, bool learned, bool learned_only) const override; + // ----------------------- // @@ -649,11 +664,12 @@ namespace sat { // // ----------------------- public: - bool check_invariant() const; + bool check_invariant() const override; void display(std::ostream & out) const; void display_watches(std::ostream & out) const; void display_watches(std::ostream & out, literal lit) const; - void display_dimacs(std::ostream & out) const; + void display_dimacs(std::ostream & out) const override; + std::ostream& display_model(std::ostream& out) const; void display_wcnf(std::ostream & out, unsigned sz, literal const* lits, unsigned const* weights) const; void display_assignment(std::ostream & out) const; std::ostream& display_justification(std::ostream & out, justification const& j) const; diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index df2da82e7..9ca1150a9 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -60,6 +60,7 @@ class inc_sat_solver : public solver { atom2bool_var m_map; scoped_ptr m_bb_rewriter; tactic_ref m_preprocess; + bool m_is_cnf; unsigned m_num_scopes; sat::literal_vector m_asms; goal_ref_buffer m_subgoals; @@ -88,6 +89,7 @@ public: m_fmls_head(0), m_core(m), m_map(m), + m_is_cnf(true), m_num_scopes(0), m_unknown("no reason given"), m_internalized_converted(false), @@ -164,7 +166,7 @@ public: (m.is_not(e, e) && is_uninterp_const(e)); } - lbool check_sat(unsigned sz, expr * const * assumptions) override { + lbool check_sat_core(unsigned sz, expr * const * assumptions) override { m_solver.pop_to_base_level(); m_core.reset(); if (m_solver.inconsistent()) return l_false; @@ -262,7 +264,19 @@ public: void assert_expr_core2(expr * t, expr * a) override { if (a) { m_asmsf.push_back(a); - assert_expr_core(m.mk_implies(a, t)); + if (m_is_cnf && is_literal(t) && is_literal(a)) { + assert_expr_core(m.mk_or(::mk_not(m, a), t)); + } + else if (m_is_cnf && m.is_or(t) && is_clause(t) && is_literal(a)) { + expr_ref_vector args(m); + args.push_back(::mk_not(m, a)); + args.append(to_app(t)->get_num_args(), to_app(t)->get_args()); + assert_expr_core(m.mk_or(args.size(), args.c_ptr())); + } + else { + m_is_cnf = false; + assert_expr_core(m.mk_implies(a, t)); + } } else { assert_expr_core(t); @@ -272,6 +286,7 @@ public: ast_manager& get_manager() const override { return m; } void assert_expr_core(expr * t) override { TRACE("goal2sat", tout << mk_pp(t, m) << "\n";); + m_is_cnf &= is_clause(t); m_fmls.push_back(t); } void set_produce_models(bool f) override {} @@ -303,6 +318,39 @@ public: r.reset(); r.append(m_core.size(), m_core.c_ptr()); } + + void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { + unsigned sz = vars.size(); + depth.resize(sz); + for (unsigned i = 0; i < sz; ++i) { + auto bv = m_map.to_bool_var(vars[i]); + depth[i] = bv == sat::null_bool_var ? UINT_MAX : m_solver.lvl(bv); + } + } + + expr_ref_vector get_trail() override { + expr_ref_vector result(m); + unsigned sz = m_solver.trail_size(); + expr_ref_vector lit2expr(m); + lit2expr.resize(m_solver.num_vars() * 2); + m_map.mk_inv(lit2expr); + for (unsigned i = 0; i < sz; ++i) { + sat::literal lit = m_solver.trail_literal(i); + result.push_back(lit2expr[lit.index()].get()); + } + return result; + } + + void set_activity(expr* var, double activity) override { + m.is_not(var, var); + sat::bool_var v = m_map.to_bool_var(var); + if (v == sat::null_bool_var) { + v = m_solver.add_var(true); + m_map.insert(var, v); + } + m_solver.set_activity(v, static_cast(activity)); + } + proof * get_proof() override { UNREACHABLE(); return nullptr; @@ -332,18 +380,6 @@ public: } sat::literal_vector lits; lbool result = m_solver.cube(vars, lits, backtrack_level); - switch (result) { - case l_true: - return last_cube(true); - case l_false: - return last_cube(false); - default: - break; - } - if (lits.empty()) { - set_reason_unknown(m_solver.get_reason_unknown()); - return expr_ref_vector(m); - } expr_ref_vector fmls(m); expr_ref_vector lit2expr(m); lit2expr.resize(m_solver.num_vars() * 2); @@ -358,6 +394,17 @@ public: vs.push_back(x); } } + switch (result) { + case l_true: + return last_cube(true); + case l_false: + return last_cube(false); + default: + break; + } + if (lits.empty()) { + set_reason_unknown(m_solver.get_reason_unknown()); + } return fmls; } @@ -546,7 +593,12 @@ private: SASSERT(!g->proofs_enabled()); TRACE("sat", m_solver.display(tout); g->display(tout);); try { - (*m_preprocess)(g, m_subgoals); + if (m_is_cnf) { + m_subgoals.push_back(g.get()); + } + else { + (*m_preprocess)(g, m_subgoals); + } } catch (tactic_exception & ex) { IF_VERBOSE(0, verbose_stream() << "exception in tactic " << ex.msg() << "\n";); @@ -706,6 +758,25 @@ private: } } + bool is_literal(expr* n) { + return is_uninterp_const(n) || (m.is_not(n, n) && is_uninterp_const(n)); + } + + bool is_clause(expr* fml) { + if (is_literal(fml)) { + return true; + } + if (!m.is_or(fml)) { + return false; + } + for (expr* n : *to_app(fml)) { + if (!is_literal(n)) { + return false; + } + } + return true; + } + lbool internalize_formulas() { if (m_fmls_head == m_fmls.size()) { return l_true; @@ -713,7 +784,8 @@ private: dep2asm_t dep2asm; goal_ref g = alloc(goal, m, true, false); // models, maybe cores are enabled for (unsigned i = m_fmls_head ; i < m_fmls.size(); ++i) { - g->assert_expr(m_fmls[i].get()); + expr* fml = m_fmls.get(i); + g->assert_expr(fml); } lbool res = internalize_goal(g, dep2asm, false); if (res != l_undef) { @@ -814,12 +886,11 @@ private: } sat::model const & ll_m = m_solver.get_model(); mdl = alloc(model, m); - for (auto const& kv : m_map) { - expr * n = kv.m_key; - if (is_app(n) && to_app(n)->get_num_args() > 0) { + for (sat::bool_var v = 0; v < ll_m.size(); ++v) { + expr* n = m_sat_mc->var2expr(v); + if (!n || !is_app(n) || to_app(n)->get_num_args() > 0) { continue; } - sat::bool_var v = kv.m_value; switch (sat::value_at(v, ll_m)) { case l_true: mdl->register_decl(to_app(n)->get_decl(), m.mk_true()); @@ -831,23 +902,23 @@ private: break; } } - //IF_VERBOSE(0, model_v2_pp(verbose_stream(), *mdl, true);); if (m_sat_mc) { - //IF_VERBOSE(0, m_sat_mc->display(verbose_stream() << "satmc\n");); + // IF_VERBOSE(0, m_sat_mc->display(verbose_stream() << "satmc\n");); (*m_sat_mc)(mdl); } if (m_mcs.back()) { //IF_VERBOSE(0, m_mc0->display(verbose_stream() << "mc0\n");); (*m_mcs.back())(mdl); } - TRACE("sat", model_smt2_pp(tout, m, *mdl, 0);); - + TRACE("sat", model_smt2_pp(tout, m, *mdl, 0);); - if (!gparams::get_ref().get_bool("model_validate", false)) return; + if (!gparams::get_ref().get_bool("model_validate", false)) { + return; + } IF_VERBOSE(1, verbose_stream() << "Verifying solution\n";); model_evaluator eval(*mdl); - eval.set_model_completion(false); + // eval.set_model_completion(false); bool all_true = true; //unsigned i = 0; for (expr * f : m_fmls) { @@ -857,14 +928,15 @@ private: tout << "Evaluation failed: " << mk_pp(f, m) << " to " << mk_pp(f, m) << "\n"; model_smt2_pp(tout, m, *(mdl.get()), 0);); if (!m.is_true(tmp)) { - IF_VERBOSE(0, verbose_stream() << "failed to verify: " << mk_pp(f, m) << "\n";); + IF_VERBOSE(0, verbose_stream() << "failed to verify: " << mk_pp(f, m) << "\n"); + IF_VERBOSE(0, verbose_stream() << "evaluated to " << tmp << "\n"); all_true = false; } //IF_VERBOSE(0, verbose_stream() << (i++) << ": " << mk_pp(f, m) << "\n"); } if (!all_true) { IF_VERBOSE(0, verbose_stream() << m_params << "\n"); - IF_VERBOSE(0, m_sat_mc->display(verbose_stream() << "sat mc\n")); + // IF_VERBOSE(0, m_sat_mc->display(verbose_stream() << "sat mc\n")); IF_VERBOSE(0, if (m_mcs.back()) m_mcs.back()->display(verbose_stream() << "mc0\n")); //IF_VERBOSE(0, m_solver.display(verbose_stream())); IF_VERBOSE(0, for (auto const& kv : m_map) verbose_stream() << mk_pp(kv.m_key, m) << " |-> " << kv.m_value << "\n"); diff --git a/src/sat/sat_solver_core.h b/src/sat/sat_solver_core.h new file mode 100644 index 000000000..b3c43ea6a --- /dev/null +++ b/src/sat/sat_solver_core.h @@ -0,0 +1,118 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + sat_solver_core.h + +Abstract: + + SAT solver API class. + +Author: + + Nikolaj Bjorner (nbjorner) 2019-02-06 + +Revision History: + +--*/ +#ifndef SAT_SOLVER_CORE_H_ +#define SAT_SOLVER_CORE_H_ + + +#include "sat/sat_types.h" + +namespace sat { + + class solver_core { + protected: + reslimit& m_rlimit; + public: + solver_core(reslimit& l) : m_rlimit(l) {} + virtual ~solver_core() {} + + virtual void pop_to_base_level() {} + virtual bool at_base_lvl() const { return true; } + + // retrieve model if solver return sat + virtual model const & get_model() const = 0; + + // retrieve core from assumptions + virtual literal_vector const& get_core() const = 0; + + // is the state inconsistent? + virtual bool inconsistent() const = 0; + + // number of variables and clauses + virtual unsigned num_vars() const = 0; + virtual unsigned num_clauses() const = 0; + + // check satisfiability + virtual lbool check(unsigned num_lits = 0, literal const* lits = nullptr) = 0; + + virtual char const* get_reason_unknown() const { return "reason unavailable"; } + + // add clauses + virtual void add_clause(unsigned n, literal* lits, bool is_redundant) = 0; + void add_clause(literal l1, literal l2, bool is_redundant) { + literal lits[2] = {l1, l2}; + add_clause(2, lits, is_redundant); + } + void add_clause(literal l1, literal l2, literal l3, bool is_redundant) { + literal lits[3] = {l1, l2, l3}; + add_clause(3, lits, is_redundant); + } + // create boolean variable, tagged as external (= true) or internal (can be eliminated). + virtual bool_var add_var(bool ext) = 0; + + // update parameters + virtual void updt_params(params_ref const& p) {} + + + virtual bool check_invariant() const { return true; } + virtual void display_status(std::ostream& out) const {} + virtual void display_dimacs(std::ostream& out) const {} + + virtual bool is_external(bool_var v) const { return true; } + bool is_external(literal l) const { return is_external(l.var()); } + virtual void set_external(bool_var v) {} + virtual void set_non_external(bool_var v) {} + virtual void set_eliminated(bool_var v, bool f) {} + + // optional support for user-scopes. Not relevant for sat_tactic integration. + // it is only relevant for incremental mode SAT, which isn't wrapped (yet) + virtual void user_push() { throw default_exception("optional API not supported"); } + virtual void user_pop(unsigned num_scopes) {}; + virtual unsigned num_user_scopes() const { return 0;} + + // hooks for extension solver. really just ba_solver atm. + virtual extension* get_extension() const { return nullptr; } + virtual void set_extension(extension* e) { if (e) throw default_exception("optional API not supported"); } + + + // The following methods are used when converting the state from the SAT solver back + // to a set of assertions. + + // retrieve model converter that handles variable elimination and other transformations + virtual void flush(model_converter& mc) {} + + // size of initial trail containing unit clauses + virtual unsigned init_trail_size() const = 0; + + // literal at trail index i + virtual literal trail_literal(unsigned i) const = 0; + + // collect n-ary clauses + virtual clause_vector const& clauses() const = 0; + + // collect binary clauses + typedef std::pair bin_clause; + virtual void collect_bin_clauses(svector & r, bool learned, bool learned_only) const = 0; + + // collect statistics from sat solver + virtual void collect_statistics(statistics & st) const {} + + }; +}; + +#endif diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index 002e49006..cbe0cab4c 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -26,6 +26,7 @@ Revision History: #include "util/common_msgs.h" #include "util/vector.h" #include "util/uint_set.h" +#include "util/stopwatch.h" #include namespace sat { @@ -220,8 +221,11 @@ namespace sat { inline std::ostream & operator<<(std::ostream & out, mem_stat const & m) { double mem = static_cast(memory::get_allocation_size())/static_cast(1024*1024); - out << " :memory " << std::fixed << std::setprecision(2) << mem; - return out; + return out << std::fixed << std::setprecision(2) << mem; + } + + inline std::ostream& operator<<(std::ostream& out, stopwatch const& sw) { + return out << " :time " << std::fixed << std::setprecision(2) << sw.get_seconds(); } struct dimacs_lit { diff --git a/src/sat/sat_unit_walk.cpp b/src/sat/sat_unit_walk.cpp index 4a3cda47c..8c7a6c6f9 100644 --- a/src/sat/sat_unit_walk.cpp +++ b/src/sat/sat_unit_walk.cpp @@ -8,6 +8,7 @@ Module Name: Abstract: unit walk local search procedure. + A variant of UnitWalk. Hirsch and Kojevinkov, SAT 2001. This version uses a trail to reset assignments and integrates directly with the watch list structure. Thus, assignments are not delayed and we avoid treating @@ -16,11 +17,9 @@ Abstract: It uses standard DPLL approach for backracking, flipping the last decision literal that lead to a conflict. It restarts after evern 100 conflicts. - It does not attempt to add conflict clauses or alternate with - walksat. + It does not attempt to add conflict clauses - It can receive conflict clauses from a concurrent CDCL solver and does not - create its own conflict clauses. + It can receive conflict clauses from a concurrent CDCL solver The phase of variables is optionally sticky between rounds. We use a decay rate to compute stickiness of a variable. @@ -31,21 +30,52 @@ Author: Revision History: + 2018-11-5: + change reinitialization to use local search with limited timeouts to find phase and ordering of variables. + --*/ -#include "sat_unit_walk.h" +#include "sat/sat_unit_walk.h" +#include "util/luby.h" + namespace sat { + bool_var unit_walk::var_priority::peek(solver& s) { + while (m_head < m_vars.size()) { + bool_var v = m_vars[m_head]; + unsigned idx = literal(v, false).index(); + if (s.m_assignment[idx] == l_undef) + return v; + ++m_head; + } + for (bool_var v : m_vars) { + if (s.m_assignment[literal(v, false).index()] == l_undef) { + IF_VERBOSE(0, verbose_stream() << "unassigned: " << v << "\n"); + } + } + IF_VERBOSE(0, verbose_stream() << "(sat.unit-walk sat)\n"); + return null_bool_var; + } + + void unit_walk::var_priority::set_vars(solver& s) { + m_vars.reset(); + for (unsigned v = 0; v < s.num_vars(); ++v) { + if (!s.was_eliminated(v) && s.m_assignment[v] == l_undef) { + add(v); + } + } + } + + bool_var unit_walk::var_priority::next(solver& s) { + bool_var v = peek(s); + ++m_head; + return v; + } + unit_walk::unit_walk(solver& s): - s(s) - { - m_runs = 0; - m_periods = 0; - m_max_runs = UINT_MAX; - m_max_periods = 5000; // UINT_MAX; // TBD configure - m_max_conflicts = 100; - m_sticky_phase = s.get_config().m_phase_sticky; + s(s) { + m_max_conflicts = 10000; m_flips = 0; } @@ -63,112 +93,254 @@ namespace sat { lbool unit_walk::operator()() { scoped_set_unit_walk _scoped_set(this, s); init_runs(); - for (m_runs = 0; m_runs < m_max_runs || m_max_runs == UINT_MAX; ++m_runs) { - init_propagation(); - init_phase(); - for (m_periods = 0; m_periods < m_max_periods || m_max_periods == UINT_MAX; ++m_periods) { - if (!s.rlimit().inc()) return l_undef; - lbool r = unit_propagation(); - if (r != l_undef) return r; - } + init_propagation(); + init_phase(); + lbool st = l_undef; + while (s.rlimit().inc() && st == l_undef) { + if (inconsistent() && !m_decisions.empty()) do_pop(); + else if (inconsistent()) st = l_false; + else if (should_restart()) restart(); + else if (should_backjump()) st = do_backjump(); + else st = decide(); } + log_status(); + return st; + } + + void unit_walk::do_pop() { + update_max_trail(); + ++s.m_stats.m_conflict; + pop(); + propagate(); + } + + lbool unit_walk::decide() { + bool_var v = pqueue().next(s); + if (v == null_bool_var) { + return l_true; + } + literal lit(v, !m_phase[v]); + ++s.m_stats.m_decision; + m_decisions.push_back(lit); + pqueue().push(); + assign(lit); + propagate(); return l_undef; } - lbool unit_walk::unit_propagation() { - init_propagation(); - while (!m_freevars.empty() && !inconsistent()) { - bool_var v = m_freevars.begin()[m_rand(m_freevars.size())]; - literal lit(v, !m_phase[v]); - ++s.m_stats.m_decision; - m_decisions.push_back(lit); - assign(lit); - propagate(); - while (inconsistent() && !m_decisions.empty()) { - ++m_conflicts; - backtrack(); - propagate(); - } - if (m_conflicts >= m_max_conflicts && !m_freevars.empty()) { - set_conflict(); - break; - } - } - if (!inconsistent()) { - log_status(); - IF_VERBOSE(1, verbose_stream() << "(sat-unit-walk sat)\n";); - s.mk_model(); - return l_true; + bool unit_walk::should_backjump() { + return + s.m_stats.m_conflict >= m_max_conflicts && m_decisions.size() > 20; + } + + lbool unit_walk::do_backjump() { + unsigned backjump_level = m_decisions.size(); // - (m_decisions.size()/20); + switch (update_priority(backjump_level)) { + case l_true: return l_true; + case l_false: break; // TBD + default: break; } + refresh_solver(); return l_undef; } + void unit_walk::pop() { + SASSERT (!m_decisions.empty()); + literal dlit = m_decisions.back(); + pop_decision(); + assign(~dlit); + } + + void unit_walk::pop_decision() { + SASSERT (!m_decisions.empty()); + literal dlit = m_decisions.back(); + literal lit; + do { + SASSERT(!m_trail.empty()); + lit = m_trail.back(); + s.m_assignment[lit.index()] = l_undef; + s.m_assignment[(~lit).index()] = l_undef; + m_trail.pop_back(); + } + while (lit != dlit); + m_qhead = m_trail.size(); + m_decisions.pop_back(); + pqueue().pop(); + m_inconsistent = false; + } + void unit_walk::init_runs() { - m_freevars.reset(); + m_luby_index = 0; + m_restart_threshold = 1000; + m_max_trail = 0; m_trail.reset(); m_decisions.reset(); m_phase.resize(s.num_vars()); - double2 d2; - d2.t = 1.0; - d2.f = 1.0; - m_phase_tf.resize(s.num_vars(), d2); - for (unsigned i = 0; i < s.num_vars(); ++i) { - literal l(i, false); - if (!s.was_eliminated(l.var()) && s.m_assignment[l.index()] == l_undef) - m_freevars.insert(l.var()); + m_phase_tf.resize(s.num_vars(), ema(1e-5)); + pqueue().reset(); + pqueue().set_vars(s); + for (unsigned v = 0; v < s.num_vars(); ++v) { + m_phase_tf[v].update(50); } - IF_VERBOSE(1, verbose_stream() << "num vars: " << s.num_vars() << " free vars: " << m_freevars.size() << "\n";); + m_ls.import(s, true); + m_rand.set_seed(s.rand()()); + update_priority(0); + } + + lbool unit_walk::do_local_search(unsigned num_rounds) { + unsigned prefix_length = (0*m_trail.size())/10; + for (unsigned v = 0; v < s.num_vars(); ++v) { + m_ls.set_bias(v, m_phase_tf[v] >= 50 ? l_true : l_false); + } + for (literal lit : m_trail) { + m_ls.set_bias(lit.var(), lit.sign() ? l_false : l_true); + } + m_ls.rlimit().push(num_rounds); + lbool is_sat = m_ls.check(prefix_length, m_trail.c_ptr(), nullptr); + m_ls.rlimit().pop(); + return is_sat; + } + + lbool unit_walk::update_priority(unsigned level) { + + while (m_decisions.size() > level) { + pop_decision(); + } + IF_VERBOSE(1, verbose_stream() << "(sat.unit-walk :update-priority " << m_decisions.size() << "\n"); + + unsigned num_rounds = 50; + log_status(); + + lbool is_sat = do_local_search(num_rounds); + switch (is_sat) { + case l_true: + for (unsigned v = 0; v < s.num_vars(); ++v) { + s.m_assignment[v] = m_ls.get_phase(v) ? l_true : l_false; + } + return l_true; + case l_false: + if (m_decisions.empty()) { + return l_false; + } + else { + pop(); + return l_undef; + } + default: + update_pqueue(); + return l_undef; + } + } + + /** + * \brief Reshuffle variables in the priority queue using the break counts from local search. + */ + struct compare_break { + local_search& ls; + compare_break(local_search& ls): ls(ls) {} + int operator()(bool_var v, bool_var w) const { + double diff = ls.break_count(v) - ls.break_count(w); + return diff > 0; + } + }; + + void unit_walk::update_pqueue() { + compare_break cb(m_ls); + std::sort(pqueue().begin(), pqueue().end(), cb); + for (bool_var v : pqueue()) { + m_phase_tf[v].update(m_ls.cur_solution(v) ? 100 : 0); + m_phase[v] = m_ls.cur_solution(v); + } + pqueue().rewind(); } void unit_walk::init_phase() { - m_max_trail = 0; - if (m_sticky_phase) { - for (bool_var v : m_freevars) { - if (s.m_phase[v] == POS_PHASE) { - m_phase[v] = true; - } - else if (s.m_phase[v] == NEG_PHASE) { - m_phase[v] = false; - } - else { - m_phase[v] = m_rand(100 * static_cast(m_phase_tf[v].t + m_phase_tf[v].f)) <= 100 * m_phase_tf[v].t; - } + for (bool_var v : pqueue()) { + if (s.m_phase[v] == POS_PHASE) { + m_phase[v] = true; + } + else if (s.m_phase[v] == NEG_PHASE) { + m_phase[v] = false; + } + else { + m_phase[v] = m_rand(100) < m_phase_tf[v]; } } - else { - for (bool_var v : m_freevars) - m_phase[v] = (m_rand(2) == 0); + } + + void unit_walk::refresh_solver() { + m_max_conflicts += m_conflict_offset ; + m_conflict_offset += 10000; + if (s.m_par && s.m_par->copy_solver(s)) { + IF_VERBOSE(1, verbose_stream() << "(sat.unit-walk fresh copy)\n";); + if (s.get_extension()) s.get_extension()->set_unit_walk(this); + init_runs(); + init_phase(); + } + } + + bool unit_walk::should_restart() { + if (s.m_stats.m_conflict >= m_restart_threshold) { + m_restart_threshold = s.get_config().m_restart_initial * get_luby(m_luby_index); + ++m_luby_index; + return true; + } + return false; + } + + void unit_walk::restart() { + while (!m_decisions.empty()) { + pop_decision(); + } + } + + void unit_walk::update_max_trail() { + if (m_max_trail == 0 || m_trail.size() > m_max_trail) { + m_max_trail = m_trail.size(); + m_restart_threshold += 10000; + m_max_conflicts = s.m_stats.m_conflict + 20000; + log_status(); } } void unit_walk::init_propagation() { if (s.m_par && s.m_par->copy_solver(s)) { - IF_VERBOSE(1, verbose_stream() << "(sat-unit-walk fresh copy)\n";); + IF_VERBOSE(1, verbose_stream() << "(sat.unit-walk fresh copy)\n";); if (s.get_extension()) s.get_extension()->set_unit_walk(this); init_runs(); init_phase(); } - if (m_max_trail == 0 || m_trail.size() > m_max_trail) { - m_max_trail = m_trail.size(); - log_status(); - } for (literal lit : m_trail) { s.m_assignment[lit.index()] = l_undef; s.m_assignment[(~lit).index()] = l_undef; - m_freevars.insert(lit.var()); } m_flips = 0; m_trail.reset(); - m_conflicts = 0; + s.m_stats.m_conflict = 0; + m_conflict_offset = 10000; m_decisions.reset(); m_qhead = 0; m_inconsistent = false; } void unit_walk::propagate() { - while (m_qhead < m_trail.size() && !inconsistent()) - propagate(choose_literal()); - // IF_VERBOSE(1, verbose_stream() << m_trail.size() << " " << inconsistent() << "\n";); + while (m_qhead < m_trail.size() && !inconsistent()) { + propagate(m_trail[m_qhead++]); + } + } + + std::ostream& unit_walk::display(std::ostream& out) const { + unsigned i = 0; + out << "num decisions: " << m_decisions.size() << "\n"; + for (literal lit : m_trail) { + if (i < m_decisions.size() && m_decisions[i] == lit) { + out << "d " << i << ": "; + ++i; + } + out << lit << "\n"; + } + s.display(verbose_stream()); + return out; } void unit_walk::propagate(literal l) { @@ -289,48 +461,36 @@ namespace sat { } void unit_walk::assign(literal lit) { - SASSERT(value(lit) == l_undef); + VERIFY(value(lit) == l_undef); s.m_assignment[lit.index()] = l_true; s.m_assignment[(~lit).index()] = l_false; m_trail.push_back(lit); - m_freevars.remove(lit.var()); if (s.get_extension() && s.is_external(lit.var())) { s.get_extension()->asserted(lit); } if (m_phase[lit.var()] == lit.sign()) { ++m_flips; flip_phase(lit); + m_phase_tf[lit.var()].update(m_phase[lit.var()] ? 100 : 0); } } void unit_walk::flip_phase(literal l) { bool_var v = l.var(); m_phase[v] = !m_phase[v]; - if (m_sticky_phase) { - m_phase_tf[v].f *= 0.98; - m_phase_tf[v].t *= 0.98; - if (m_phase[v]) m_phase_tf[v].t += 1; else m_phase_tf[v].f += 1; - } } void unit_walk::log_status() { - IF_VERBOSE(1, verbose_stream() << "(sat-unit-walk :trail " << m_max_trail - << " :branches " << m_decisions.size() - << " :free " << m_freevars.size() - << " :periods " << m_periods + IF_VERBOSE(1, verbose_stream() + << "(sat.unit-walk" + << " :trail " << m_trail.size() + << " :depth " << m_decisions.size() << " :decisions " << s.m_stats.m_decision << " :propagations " << s.m_stats.m_propagate + << " :conflicts " << s.m_stats.m_conflict << ")\n";); } - literal unit_walk::choose_literal() { - SASSERT(m_qhead < m_trail.size()); - unsigned idx = m_rand(m_trail.size() - m_qhead); - std::swap(m_trail[m_qhead], m_trail[m_qhead + idx]); - literal lit = m_trail[m_qhead++]; - return lit; - } - void unit_walk::set_conflict(literal l1, literal l2) { set_conflict(); } @@ -346,25 +506,6 @@ namespace sat { void unit_walk::set_conflict() { m_inconsistent = true; } - - void unit_walk::backtrack() { - if (m_decisions.empty()) return; - literal dlit = m_decisions.back(); - literal lit; - do { - SASSERT(!m_trail.empty()); - lit = m_trail.back(); - s.m_assignment[lit.index()] = l_undef; - s.m_assignment[(~lit).index()] = l_undef; - m_freevars.insert(lit.var()); - m_trail.pop_back(); - } - while (lit != dlit); - m_inconsistent = false; - m_decisions.pop_back(); - m_qhead = m_trail.size(); - assign(~dlit); - } }; diff --git a/src/sat/sat_unit_walk.h b/src/sat/sat_unit_walk.h index 8ab9bab70..14588a63c 100644 --- a/src/sat/sat_unit_walk.h +++ b/src/sat/sat_unit_walk.h @@ -20,51 +20,83 @@ Revision History: #define SAT_UNIT_WALK_H_ #include "sat/sat_solver.h" +#include "sat/sat_local_search.h" +#include "util/ema.h" namespace sat { class unit_walk { +#if 0 struct double2 { double t, f; }; +#endif + class var_priority { + svector m_vars; + unsigned_vector m_lim; + unsigned m_head; + public: + var_priority() { m_head = 0; } + void reset() { m_lim.reset(); m_head = 0;} + void rewind() { for (unsigned& l : m_lim) l = 0; m_head = 0;} + void add(bool_var v) { m_vars.push_back(v); } + bool_var next(solver& s); + bool_var peek(solver& s); + void set_vars(solver& s); + void push() { m_lim.push_back(m_head); } + void pop() { m_head = m_lim.back(); m_lim.pop_back(); } + bool empty() const { return m_lim.empty(); } + bool_var const* begin() const { return m_vars.begin(); } + bool_var const* end() const { return m_vars.end(); } + bool_var* begin() { return m_vars.begin(); } + bool_var* end() { return m_vars.end(); } + }; + solver& s; + local_search m_ls; random_gen m_rand; svector m_phase; - svector m_phase_tf; - indexed_uint_set m_freevars; - unsigned m_runs; - unsigned m_periods; + svector m_phase_tf; + var_priority m_priorities; + unsigned m_luby_index; + unsigned m_restart_threshold; // settings - unsigned m_max_runs; - unsigned m_max_periods; unsigned m_max_conflicts; - bool m_sticky_phase; - unsigned m_propagations; unsigned m_flips; unsigned m_max_trail; unsigned m_qhead; literal_vector m_trail; bool m_inconsistent; literal_vector m_decisions; - unsigned m_conflicts; + unsigned m_conflict_offset; - void push(); - void backtrack(); + bool should_restart(); + void do_pop(); + bool should_backjump(); + lbool do_backjump(); + lbool do_local_search(unsigned num_rounds); + lbool decide(); + void restart(); + void pop(); + void pop_decision(); void init_runs(); + lbool update_priority(unsigned level); + void update_pqueue(); void init_phase(); void init_propagation(); + void refresh_solver(); + void update_max_trail(); void flip_phase(literal l); - lbool unit_propagation(); void propagate(); void propagate(literal lit); - literal choose_literal(); void set_conflict(literal l1, literal l2); void set_conflict(literal l1, literal l2, literal l3); void set_conflict(clause const& c); inline lbool value(literal lit) { return s.value(lit); } void log_status(); + var_priority& pqueue() { return m_priorities; } public: unit_walk(solver& s); diff --git a/src/sat/sat_watched.cpp b/src/sat/sat_watched.cpp index 199d12797..966bd8325 100644 --- a/src/sat/sat_watched.cpp +++ b/src/sat/sat_watched.cpp @@ -18,6 +18,7 @@ Revision History: --*/ #include "sat/sat_watched.h" #include "sat/sat_clause.h" +#include "sat/sat_extension.h" namespace sat { @@ -96,7 +97,7 @@ namespace sat { } - std::ostream& display_watch_list(std::ostream & out, clause_allocator const & ca, watch_list const & wlist) { + std::ostream& display_watch_list(std::ostream & out, clause_allocator const & ca, watch_list const & wlist, extension* ext) { bool first = true; for (watched const& w : wlist) { if (first) @@ -116,7 +117,12 @@ namespace sat { out << "(" << w.get_blocked_literal() << " " << *(ca.get_clause(w.get_clause_offset())) << ")"; break; case watched::EXT_CONSTRAINT: - out << "ext: " << w.get_ext_constraint_idx(); + if (ext) { + ext->display_constraint(out, w.get_ext_constraint_idx()); + } + else { + out << "ext: " << w.get_ext_constraint_idx(); + } break; default: UNREACHABLE(); diff --git a/src/sat/sat_watched.h b/src/sat/sat_watched.h index 09ad22ffd..a05e7c365 100644 --- a/src/sat/sat_watched.h +++ b/src/sat/sat_watched.h @@ -35,6 +35,9 @@ namespace sat { Remark: there are no clause objects for binary clauses. */ + + class extension; + class watched { public: enum kind { @@ -138,7 +141,7 @@ namespace sat { void set_ternary_learned(watch_list& wlist, literal l1, literal l2, bool learned); class clause_allocator; - std::ostream& display_watch_list(std::ostream & out, clause_allocator const & ca, watch_list const & wlist); + std::ostream& display_watch_list(std::ostream & out, clause_allocator const & ca, watch_list const & wlist, extension* ext); void conflict_cleanup(watch_list::iterator it, watch_list::iterator it2, watch_list& wlist); }; diff --git a/src/sat/tactic/atom2bool_var.cpp b/src/sat/tactic/atom2bool_var.cpp index b79eaa251..cd6ab776d 100644 --- a/src/sat/tactic/atom2bool_var.cpp +++ b/src/sat/tactic/atom2bool_var.cpp @@ -38,9 +38,13 @@ void atom2bool_var::mk_var_inv(app_ref_vector & var2expr) const { } sat::bool_var atom2bool_var::to_bool_var(expr * n) const { - sat::bool_var v = sat::null_bool_var; - m_mapping.find(n, v); - return v; + unsigned idx = m_id2map.get(n->get_id(), UINT_MAX); + if (idx == UINT_MAX) { + return sat::null_bool_var; + } + else { + return m_mapping[idx].m_value; + } } struct collect_boolean_interface_proc { diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 68e18375f..b9adba7cf 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -57,10 +57,10 @@ struct goal2sat::imp { svector m_result_stack; obj_map m_cache; obj_hashtable m_interface_vars; - sat::solver & m_solver; + sat::solver_core & m_solver; atom2bool_var & m_map; dep2asm_map & m_dep2asm; - sat::bool_var m_true; + sat::literal m_true; bool m_ite_extra; unsigned long long m_max_memory; expr_ref_vector m_trail; @@ -69,7 +69,7 @@ struct goal2sat::imp { bool m_xor_solver; bool m_is_lemma; - imp(ast_manager & _m, params_ref const & p, sat::solver & s, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external): + imp(ast_manager & _m, params_ref const & p, sat::solver_core & s, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external): m(_m), pb(m), m_ext(nullptr), @@ -81,14 +81,14 @@ struct goal2sat::imp { m_default_external(default_external), m_is_lemma(false) { updt_params(p); - m_true = sat::null_bool_var; - mk_true(); + m_true = sat::null_literal; } void updt_params(params_ref const & p) { m_ite_extra = p.get_bool("ite_extra", true); m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_xor_solver = p.get_bool("xor_solver", false); + if (m_xor_solver) ensure_extension(); } void throw_op_not_handled(std::string const& s) { @@ -98,31 +98,31 @@ struct goal2sat::imp { void mk_clause(sat::literal l) { TRACE("goal2sat", tout << "mk_clause: " << l << "\n";); - m_solver.mk_clause(1, &l); + m_solver.add_clause(1, &l, false); } void set_lemma_mode(bool f) { m_is_lemma = f; } void mk_clause(sat::literal l1, sat::literal l2) { TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << "\n";); - m_solver.mk_clause(l1, l2, m_is_lemma); + m_solver.add_clause(l1, l2, m_is_lemma); } void mk_clause(sat::literal l1, sat::literal l2, sat::literal l3) { TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << " " << l3 << "\n";); - m_solver.mk_clause(l1, l2, l3, m_is_lemma); + m_solver.add_clause(l1, l2, l3, m_is_lemma); } void mk_clause(unsigned num, sat::literal * lits) { TRACE("goal2sat", tout << "mk_clause: "; for (unsigned i = 0; i < num; i++) tout << lits[i] << " "; tout << "\n";); - m_solver.mk_clause(num, lits, m_is_lemma); + m_solver.add_clause(num, lits, m_is_lemma); } - sat::bool_var mk_true() { - if (m_true == sat::null_bool_var) { + sat::literal mk_true() { + if (m_true == sat::null_literal) { // create fake variable to represent true; - m_true = m_solver.mk_var(false); - mk_clause(sat::literal(m_true, false)); // v is true + m_true = sat::literal(m_solver.add_var(false), false); + mk_clause(m_true); // v is true } return m_true; } @@ -133,14 +133,14 @@ struct goal2sat::imp { sat::bool_var v = m_map.to_bool_var(t); if (v == sat::null_bool_var) { if (m.is_true(t)) { - l = sat::literal(mk_true(), sign); + l = sign ? ~mk_true() : mk_true(); } else if (m.is_false(t)) { - l = sat::literal(mk_true(), !sign); + l = sign ? mk_true() : ~mk_true(); } else { bool ext = m_default_external || !is_uninterp_const(t) || m_interface_vars.contains(t); - sat::bool_var v = m_solver.mk_var(ext); + sat::bool_var v = m_solver.add_var(ext); m_map.insert(t, v); l = sat::literal(v, sign); TRACE("sat", tout << "new_var: " << v << ": " << mk_ismt2_pp(t, m) << "\n";); @@ -248,7 +248,7 @@ struct goal2sat::imp { } else { SASSERT(num <= m_result_stack.size()); - sat::bool_var k = m_solver.mk_var(); + sat::bool_var k = m_solver.add_var(false); sat::literal l(k, false); m_cache.insert(t, l); sat::literal * lits = m_result_stack.end() - num; @@ -287,7 +287,7 @@ struct goal2sat::imp { } else { SASSERT(num <= m_result_stack.size()); - sat::bool_var k = m_solver.mk_var(); + sat::bool_var k = m_solver.add_var(false); sat::literal l(k, false); m_cache.insert(t, l); // l => /\ lits @@ -330,7 +330,7 @@ struct goal2sat::imp { m_result_stack.reset(); } else { - sat::bool_var k = m_solver.mk_var(); + sat::bool_var k = m_solver.add_var(false); sat::literal l(k, false); m_cache.insert(n, l); mk_clause(~l, ~c, t); @@ -367,7 +367,7 @@ struct goal2sat::imp { m_result_stack.reset(); } else { - sat::bool_var k = m_solver.mk_var(); + sat::bool_var k = m_solver.add_var(false); sat::literal l(k, false); m_cache.insert(t, l); mk_clause(~l, l1, ~l2); @@ -391,7 +391,7 @@ struct goal2sat::imp { return; } sat::literal_vector lits; - sat::bool_var v = m_solver.mk_var(true); + sat::bool_var v = m_solver.add_var(true); lits.push_back(sat::literal(v, true)); convert_pb_args(num, lits); // ensure that = is converted to xor @@ -473,7 +473,7 @@ struct goal2sat::imp { m_ext->add_pb_ge(sat::null_bool_var, wlits, k1); } else { - sat::bool_var v = m_solver.mk_var(true); + sat::bool_var v = m_solver.add_var(true); sat::literal lit(v, sign); m_ext->add_pb_ge(v, wlits, k.get_unsigned()); TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); @@ -504,7 +504,7 @@ struct goal2sat::imp { m_ext->add_pb_ge(sat::null_bool_var, wlits, k1); } else { - sat::bool_var v = m_solver.mk_var(true); + sat::bool_var v = m_solver.add_var(true); sat::literal lit(v, sign); m_ext->add_pb_ge(v, wlits, k.get_unsigned()); TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); @@ -519,8 +519,8 @@ struct goal2sat::imp { svector wlits; convert_pb_args(t, wlits); bool base_assert = (root && !sign && m_solver.num_user_scopes() == 0); - sat::bool_var v1 = base_assert ? sat::null_bool_var : m_solver.mk_var(true); - sat::bool_var v2 = base_assert ? sat::null_bool_var : m_solver.mk_var(true); + sat::bool_var v1 = base_assert ? sat::null_bool_var : m_solver.add_var(true); + sat::bool_var v2 = base_assert ? sat::null_bool_var : m_solver.add_var(true); m_ext->add_pb_ge(v1, wlits, k.get_unsigned()); k.neg(); for (wliteral& wl : wlits) { @@ -534,7 +534,7 @@ struct goal2sat::imp { } else { sat::literal l1(v1, false), l2(v2, false); - sat::bool_var v = m_solver.mk_var(); + sat::bool_var v = m_solver.add_var(false); sat::literal l(v, false); mk_clause(~l, l1); mk_clause(~l, l2); @@ -554,7 +554,7 @@ struct goal2sat::imp { m_ext->add_at_least(sat::null_bool_var, lits, k.get_unsigned()); } else { - sat::bool_var v = m_solver.mk_var(true); + sat::bool_var v = m_solver.add_var(true); sat::literal lit(v, false); m_ext->add_at_least(v, lits, k.get_unsigned()); m_cache.insert(t, lit); @@ -576,7 +576,7 @@ struct goal2sat::imp { m_ext->add_at_least(sat::null_bool_var, lits, lits.size() - k.get_unsigned()); } else { - sat::bool_var v = m_solver.mk_var(true); + sat::bool_var v = m_solver.add_var(true); sat::literal lit(v, false); m_ext->add_at_least(v, lits, lits.size() - k.get_unsigned()); m_cache.insert(t, lit); @@ -589,8 +589,8 @@ struct goal2sat::imp { SASSERT(k.is_unsigned()); sat::literal_vector lits; convert_pb_args(t->get_num_args(), lits); - sat::bool_var v1 = (root && !sign) ? sat::null_bool_var : m_solver.mk_var(true); - sat::bool_var v2 = (root && !sign) ? sat::null_bool_var : m_solver.mk_var(true); + sat::bool_var v1 = (root && !sign) ? sat::null_bool_var : m_solver.add_var(true); + sat::bool_var v2 = (root && !sign) ? sat::null_bool_var : m_solver.add_var(true); m_ext->add_at_least(v1, lits, k.get_unsigned()); for (sat::literal& l : lits) { l.neg(); @@ -603,7 +603,7 @@ struct goal2sat::imp { } else { sat::literal l1(v1, false), l2(v2, false); - sat::bool_var v = m_solver.mk_var(); + sat::bool_var v = m_solver.add_var(false); sat::literal l(v, false); mk_clause(~l, l1); mk_clause(~l, l2); @@ -898,7 +898,7 @@ struct goal2sat::scoped_set_imp { }; -void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external, bool is_lemma) { +void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver_core & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external, bool is_lemma) { imp proc(g.m(), p, t, m, dep2asm, default_external); scoped_set_imp set(this, &proc); proc.set_lemma_mode(is_lemma); @@ -917,7 +917,7 @@ void goal2sat::get_interpreted_atoms(expr_ref_vector& atoms) { sat2goal::mc::mc(ast_manager& m): m(m), m_var2expr(m) {} -void sat2goal::mc::flush_smc(sat::solver& s, atom2bool_var const& map) { +void sat2goal::mc::flush_smc(sat::solver_core& s, atom2bool_var const& map) { s.flush(m_smc); m_var2expr.resize(s.num_vars()); map.mk_var_inv(m_var2expr); @@ -1158,13 +1158,14 @@ struct sat2goal::imp { r.assert_expr(fml); } - void assert_clauses(ref& mc, sat::solver const & s, sat::clause_vector const& clauses, goal & r, bool asserted) { + void assert_clauses(ref& mc, sat::solver_core const & s, sat::clause_vector const& clauses, goal & r, bool asserted) { ptr_buffer lits; + unsigned small_lbd = 3; // s.get_config().m_gc_small_lbd; for (sat::clause* cp : clauses) { checkpoint(); lits.reset(); sat::clause const & c = *cp; - if (asserted || m_learned || c.glue() <= s.get_config().m_gc_small_lbd) { + if (asserted || m_learned || c.glue() <= small_lbd) { for (sat::literal l : c) { lits.push_back(lit2expr(mc, l)); } @@ -1173,11 +1174,11 @@ struct sat2goal::imp { } } - sat::ba_solver* get_ba_solver(sat::solver const& s) { + sat::ba_solver* get_ba_solver(sat::solver_core const& s) { return dynamic_cast(s.get_extension()); } - void operator()(sat::solver & s, atom2bool_var const & map, goal & r, ref & mc) { + void operator()(sat::solver_core & s, atom2bool_var const & map, goal & r, ref & mc) { if (s.at_base_lvl() && s.inconsistent()) { r.assert_expr(m.mk_false()); return; @@ -1197,7 +1198,7 @@ struct sat2goal::imp { // collect binary clauses svector bin_clauses; - s.collect_bin_clauses(bin_clauses, m_learned); + s.collect_bin_clauses(bin_clauses, m_learned, false); for (sat::solver::bin_clause const& bc : bin_clauses) { checkpoint(); r.assert_expr(m.mk_or(lit2expr(mc, bc.first), lit2expr(mc, bc.second))); @@ -1263,7 +1264,7 @@ struct sat2goal::scoped_set_imp { } }; -void sat2goal::operator()(sat::solver & t, atom2bool_var const & m, params_ref const & p, +void sat2goal::operator()(sat::solver_core & t, atom2bool_var const & m, params_ref const & p, goal & g, ref & mc) { imp proc(g.m(), p); scoped_set_imp set(this, &proc); diff --git a/src/sat/tactic/goal2sat.h b/src/sat/tactic/goal2sat.h index 514b65311..78884051e 100644 --- a/src/sat/tactic/goal2sat.h +++ b/src/sat/tactic/goal2sat.h @@ -62,7 +62,7 @@ public: \warning conversion throws a tactic_exception, if it is interrupted (by set_cancel), an unsupported operator is found, or memory consumption limit is reached (set with param :max-memory). */ - void operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false, bool is_lemma = false); + void operator()(goal const & g, params_ref const & p, sat::solver_core & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false, bool is_lemma = false); void get_interpreted_atoms(expr_ref_vector& atoms); @@ -88,7 +88,7 @@ public: mc(ast_manager& m); ~mc() override {} // flush model converter from SAT solver to this structure. - void flush_smc(sat::solver& s, atom2bool_var const& map); + void flush_smc(sat::solver_core& s, atom2bool_var const& map); void operator()(model_ref& md) override; void operator()(expr_ref& fml) override; model_converter* translate(ast_translation& translator) override; @@ -113,7 +113,7 @@ public: \warning conversion throws a tactic_exception, if it is interrupted (by set_cancel), or memory consumption limit is reached (set with param :max-memory). */ - void operator()(sat::solver & t, atom2bool_var const & m, params_ref const & p, goal & s, ref & mc); + void operator()(sat::solver_core & t, atom2bool_var const & m, params_ref const & p, goal & s, ref & mc); }; diff --git a/src/sat/tactic/sat_tactic.cpp b/src/sat/tactic/sat_tactic.cpp index ef8a9e77e..2739932e6 100644 --- a/src/sat/tactic/sat_tactic.cpp +++ b/src/sat/tactic/sat_tactic.cpp @@ -21,6 +21,7 @@ Notes: #include "tactic/tactical.h" #include "sat/tactic/goal2sat.h" #include "sat/sat_solver.h" +#include "sat/sat_params.hpp" class sat_tactic : public tactic { @@ -28,14 +29,15 @@ class sat_tactic : public tactic { ast_manager & m; goal2sat m_goal2sat; sat2goal m_sat2goal; - sat::solver m_solver; + scoped_ptr m_solver; params_ref m_params; imp(ast_manager & _m, params_ref const & p): m(_m), - m_solver(p, m.limit()), + m_solver(alloc(sat::solver, p, m.limit())), m_params(p) { SASSERT(!m.proofs_enabled()); + updt_params(p); } void operator()(goal_ref const & g, @@ -49,7 +51,7 @@ class sat_tactic : public tactic { atom2bool_var map(m); obj_map dep2asm; sat::literal_vector assumptions; - m_goal2sat(*g, m_params, m_solver, map, dep2asm); + m_goal2sat(*g, m_params, *m_solver, map, dep2asm); TRACE("sat_solver_unknown", tout << "interpreted_atoms: " << map.interpreted_atoms() << "\n"; for (auto const& kv : map) { if (!is_uninterp_const(kv.m_key)) @@ -58,15 +60,16 @@ class sat_tactic : public tactic { g->reset(); g->m().compact_memory(); - CASSERT("sat_solver", m_solver.check_invariant()); - IF_VERBOSE(TACTIC_VERBOSITY_LVL, m_solver.display_status(verbose_stream());); - TRACE("sat_dimacs", m_solver.display_dimacs(tout);); + CASSERT("sat_solver", m_solver->check_invariant()); + IF_VERBOSE(TACTIC_VERBOSITY_LVL, m_solver->display_status(verbose_stream());); + TRACE("sat_dimacs", m_solver->display_dimacs(tout);); dep2assumptions(dep2asm, assumptions); - lbool r = m_solver.check(assumptions.size(), assumptions.c_ptr()); + lbool r = m_solver->check(assumptions.size(), assumptions.c_ptr()); + TRACE("sat", tout << "result of checking: " << r << " " << m_solver->get_reason_unknown() << "\n";); if (r == l_false) { expr_dependency * lcore = nullptr; if (produce_core) { - sat::literal_vector const& ucore = m_solver.get_core(); + sat::literal_vector const& ucore = m_solver->get_core(); u_map asm2dep; mk_asm2dep(dep2asm, asm2dep); for (unsigned i = 0; i < ucore.size(); ++i) { @@ -81,7 +84,7 @@ class sat_tactic : public tactic { // register model if (produce_models) { model_ref md = alloc(model, m); - sat::model const & ll_m = m_solver.get_model(); + sat::model const & ll_m = m_solver->get_model(); TRACE("sat_tactic", for (unsigned i = 0; i < ll_m.size(); i++) tout << i << ":" << ll_m[i] << " "; tout << "\n";); for (auto const& kv : map) { expr * n = kv.m_key; @@ -105,11 +108,11 @@ class sat_tactic : public tactic { else { // get simplified problem. #if 0 - IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "\"formula constains interpreted atoms, recovering formula from sat solver...\"\n";); + IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "\"formula constrains interpreted atoms, recovering formula from sat solver...\"\n";); #endif - m_solver.pop_to_base_level(); + m_solver->pop_to_base_level(); ref mc; - m_sat2goal(m_solver, map, m_params, *(g.get()), mc); + m_sat2goal(*m_solver, map, m_params, *(g.get()), mc); g->add(mc.get()); } g->inc_depth(); @@ -130,13 +133,20 @@ class sat_tactic : public tactic { lit2asm.insert(kv.m_value.index(), kv.m_key); } } + + void updt_params(params_ref const& p) { + m_solver->updt_params(p); + } + }; + struct scoped_set_imp { sat_tactic * m_owner; scoped_set_imp(sat_tactic * o, imp * i):m_owner(o) { - m_owner->m_imp = i; + m_owner->m_imp = i; + m_owner->updt_params(m_owner->m_params); } ~scoped_set_imp() { @@ -152,6 +162,8 @@ public: sat_tactic(ast_manager & m, params_ref const & p): m_imp(nullptr), m_params(p) { + sat_params p1(p); + m_params.set_bool("xor_solver", p1.xor_solver()); } tactic * translate(ast_manager & m) override { @@ -164,6 +176,9 @@ public: void updt_params(params_ref const & p) override { m_params = p; + sat_params p1(p); + m_params.set_bool("xor_solver", p1.xor_solver()); + if (m_imp) m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { @@ -178,12 +193,16 @@ public: scoped_set_imp set(this, &proc); try { proc(g, result); - proc.m_solver.collect_statistics(m_stats); + proc.m_solver->collect_statistics(m_stats); } catch (sat::solver_exception & ex) { - proc.m_solver.collect_statistics(m_stats); + proc.m_solver->collect_statistics(m_stats); throw tactic_exception(ex.msg()); } + catch (z3_exception& ex) { + TRACE("sat", tout << ex.msg() << "\n";); + throw; + } TRACE("sat_stats", m_stats.display_smt2(tout);); } diff --git a/src/shell/dimacs_frontend.cpp b/src/shell/dimacs_frontend.cpp index 738566ce2..4f032c01e 100644 --- a/src/shell/dimacs_frontend.cpp +++ b/src/shell/dimacs_frontend.cpp @@ -23,23 +23,35 @@ Revision History: #include "util/rlimit.h" #include "util/gparams.h" #include "sat/dimacs.h" +#include "sat/sat_params.hpp" #include "sat/sat_solver.h" +#include "sat/ba_solver.h" +#include "sat/tactic/goal2sat.h" +#include "ast/reg_decl_plugins.h" +#include "tactic/tactic.h" +#include "tactic/fd_solver/fd_solver.h" + extern bool g_display_statistics; static sat::solver * g_solver = nullptr; static clock_t g_start_time; +static tactic_ref g_tac; +static statistics g_st; static void display_statistics() { clock_t end_time = clock(); + if (g_tac && g_display_statistics) { + g_tac->collect_statistics(g_st); + } if (g_solver && g_display_statistics) { std::cout.flush(); std::cerr.flush(); - statistics st; - g_solver->collect_statistics(st); - st.update("total time", ((static_cast(end_time) - static_cast(g_start_time)) / CLOCKS_PER_SEC)); - st.display_smt2(std::cout); + g_solver->collect_statistics(g_st); + g_st.update("total time", ((static_cast(end_time) - static_cast(g_start_time)) / CLOCKS_PER_SEC)); + g_st.display_smt2(std::cout); } + g_display_statistics = false; } static void on_timeout() { @@ -101,7 +113,7 @@ static void track_clauses(sat::solver const& src, sat::clause * const * it = src.begin_clauses(); sat::clause * const * end = src.end_clauses(); svector bin_clauses; - src.collect_bin_clauses(bin_clauses, false); + src.collect_bin_clauses(bin_clauses, false, false); tracking_clauses.reserve(2*src.num_vars() + static_cast(end - it) + bin_clauses.size()); for (sat::bool_var v = 1; v < src.num_vars(); ++v) { @@ -162,14 +174,63 @@ void verify_solution(char const * file_name) { } } +lbool solve_parallel(sat::solver& s) { + params_ref p = gparams::get_module("sat"); + ast_manager m; + reg_decl_plugins(m); + sat2goal s2g; + ref mc; + atom2bool_var a2b(m); + for (unsigned v = 0; v < s.num_vars(); ++v) { + a2b.insert(m.mk_const(symbol(v), m.mk_bool_sort()), v); + } + goal_ref g = alloc(goal, m); + s2g(s, a2b, p, *g, mc); + + g_tac = mk_parallel_qffd_tactic(m, p); + std::string reason_unknown; + model_ref md; + labels_vec labels; + proof_ref pr(m); + expr_dependency_ref core(m); + lbool r = check_sat(*g_tac, g, md, labels, pr, core, reason_unknown); + switch (r) { + case l_true: + if (gparams::get_ref().get_bool("model_validate", false)) { + // populate the SAT solver with the model obtained from parallel execution. + for (auto const& kv : a2b) { + sat::literal lit; + bool is_true = m.is_true((*md)(kv.m_key)); + lit = sat::literal(kv.m_value, !is_true); + s.mk_clause(1, &lit); + } + // VERIFY(l_true == s.check()); + } + break; + case l_false: + break; + default: + break; + } + display_statistics(); + g_display_statistics = false; + g_tac = nullptr; + return r; +} + unsigned read_dimacs(char const * file_name) { g_start_time = clock(); register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); params_ref p = gparams::get_module("sat"); + params_ref par = gparams::get_module("parallel"); p.set_bool("produce_models", true); + sat_params sp(p); reslimit limit; sat::solver solver(p, limit); + if (sp.xor_solver()) { + solver.set_extension(alloc(sat::ba_solver)); + } g_solver = &solver; if (file_name) { @@ -187,20 +248,27 @@ unsigned read_dimacs(char const * file_name) { lbool r; vector tracking_clauses; - sat::solver solver2(p, limit); + params_ref p2; + p2.copy(p); + p2.set_sym("drat.file", symbol::null); + + sat::solver solver2(p2, limit); if (p.get_bool("dimacs.core", false)) { g_solver = &solver2; sat::literal_vector assumptions; track_clauses(solver, solver2, assumptions, tracking_clauses); r = g_solver->check(assumptions.size(), assumptions.c_ptr()); } + else if (par.get_bool("enable", false)) { + r = solve_parallel(solver); + } else { r = g_solver->check(); } switch (r) { case l_true: std::cout << "sat\n"; - if (file_name) verify_solution(file_name); + if (file_name && gparams::get_ref().get_bool("model_validate", false)) verify_solution(file_name); display_model(*g_solver); break; case l_undef: @@ -213,7 +281,6 @@ unsigned read_dimacs(char const * file_name) { } break; } - if (g_display_statistics) - display_statistics(); + display_statistics(); return 0; } diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 3b97d4462..260cf8a79 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -60,7 +60,7 @@ void error(const char * msg) { void display_usage() { std::cout << "Z3 [version " << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER; std::cout << " - "; -#ifdef _AMD64_ +#if defined(__LP64__) || defined(_WIN64) std::cout << "64"; #else std::cout << "32"; @@ -115,6 +115,7 @@ void display_usage() { } void parse_cmd_line_args(int argc, char ** argv) { + long timeout = 0; int i = 1; char * eq_pos = nullptr; while (i < argc) { @@ -161,7 +162,7 @@ void parse_cmd_line_args(int argc, char ** argv) { if (strcmp(opt_name, "version") == 0) { std::cout << "Z3 version " << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER; std::cout << " - "; -#ifdef _AMD64_ +#if defined(__LP64__) || defined(_WIN64) std::cout << "64"; #else std::cout << "32"; @@ -216,8 +217,7 @@ void parse_cmd_line_args(int argc, char ** argv) { else if (strcmp(opt_name, "T") == 0) { if (!opt_arg) error("option argument (-T:timeout) is missing."); - long tm = strtol(opt_arg, nullptr, 10); - set_timeout(tm * 1000); + timeout = strtol(opt_arg, nullptr, 10); } else if (strcmp(opt_name, "t") == 0) { if (!opt_arg) @@ -292,6 +292,9 @@ void parse_cmd_line_args(int argc, char ** argv) { } i++; } + + if (timeout) + set_timeout(timeout * 1000); } diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index 7218558fa..0f156bced 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -73,7 +73,6 @@ unsigned read_smtlib2_commands(char const * file_name) { cmd_context ctx; ctx.set_solver_factory(mk_smt_strategic_solver_factory()); - ctx.set_interpolating_solver_factory(mk_smt_solver_factory()); install_dl_cmds(ctx); install_dbg_cmds(ctx); diff --git a/src/smt/arith_eq_solver.cpp b/src/smt/arith_eq_solver.cpp index 4b1c6e4a6..677132804 100644 --- a/src/smt/arith_eq_solver.cpp +++ b/src/smt/arith_eq_solver.cpp @@ -608,8 +608,8 @@ bool arith_eq_solver::solve_integer_equations_gcd( return false; } live.erase(live.begin()+live_pos); - for (j = 0; j < live.size(); ++j) { - row& r = rows[live[j]]; + for (unsigned l : live) { + row& r = rows[l]; if (!r[i].is_zero()) { substitute(r, r0, i); gcd_normalize(r); @@ -625,10 +625,7 @@ bool arith_eq_solver::solve_integer_equations_gcd( TRACE("arith_eq_solver", tout << ((live.size()<=1)?"solved ":"incomplete check ") << live.size() << "\n"; - for (unsigned i = 0; i < live.size(); ++i) { - print_row(tout, rows[live[i]]); - } - ); + for (unsigned l : live) print_row(tout, rows[l]); ); return true; } diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index d364404da..164d36ae2 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -449,7 +449,7 @@ void asserted_formulas::propagate_values() { m_expr2depth.reset(); m_scoped_substitution.push(); unsigned prop = num_prop; - TRACE("propagate_values", tout << "before:\n"; display(tout);); + TRACE("propagate_values", display(tout << "before:\n");); unsigned i = m_qhead; unsigned sz = m_formulas.size(); for (; i < sz; i++) { @@ -482,15 +482,13 @@ unsigned asserted_formulas::propagate_values(unsigned i) { expr_ref new_n(m); proof_ref new_pr(m); m_rewriter(n, new_n, new_pr); + TRACE("propagate_values", tout << n << "\n" << new_n << "\n";); if (m.proofs_enabled()) { proof * pr = m_formulas[i].get_proof(); new_pr = m.mk_modus_ponens(pr, new_pr); } justified_expr j(m, new_n, new_pr); m_formulas[i] = j; - if (m_formulas[i].get_fml() != new_n) { - std::cout << "NOT updated\n"; - } if (m.is_false(j.get_fml())) { m_inconsistent = true; } diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index a6b5e5515..836c1dfcc 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -3915,7 +3915,7 @@ namespace smt { SASSERT(bindings[i]->get_generation() <= max_generation); } #endif - unsigned min_gen, max_gen; + unsigned min_gen = 0, max_gen = 0; m_interpreter.get_min_max_top_generation(min_gen, max_gen); m_context.add_instance(qa, pat, num_bindings, bindings, nullptr, max_generation, min_gen, max_gen, used_enodes); } diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 6b4aee1c3..53249e1f0 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -58,8 +58,6 @@ def_module_params(module_name='smt', ('arith.auto_config_simplex', BOOL, False, 'force simplex solver in auto_config'), ('pb.conflict_frequency', UINT, 1000, 'conflict frequency for Pseudo-Boolean theory'), ('pb.learn_complements', BOOL, True, 'learn complement literals for Pseudo-Boolean theory'), - ('pb.enable_compilation', BOOL, True, 'enable compilation into sorting circuits for Pseudo-Boolean'), - ('pb.enable_simplex', BOOL, False, 'enable simplex to check rational feasibility'), ('array.weak', BOOL, False, 'weak array theory'), ('array.extensional', BOOL, True, 'extensional array theory'), ('dack', UINT, 1, '0 - disable dynamic ackermannization, 1 - expand Leibniz\'s axiom if a congruence is the root of a conflict, 2 - expand Leibniz\'s axiom if a congruence is used during conflict resolution'), diff --git a/src/smt/params/theory_pb_params.cpp b/src/smt/params/theory_pb_params.cpp index b285429d9..45a6ede10 100644 --- a/src/smt/params/theory_pb_params.cpp +++ b/src/smt/params/theory_pb_params.cpp @@ -23,8 +23,6 @@ void theory_pb_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_pb_conflict_frequency = p.pb_conflict_frequency(); m_pb_learn_complements = p.pb_learn_complements(); - m_pb_enable_compilation = p.pb_enable_compilation(); - m_pb_enable_simplex = p.pb_enable_simplex(); } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; @@ -32,6 +30,4 @@ void theory_pb_params::updt_params(params_ref const & _p) { void theory_pb_params::display(std::ostream & out) const { DISPLAY_PARAM(m_pb_conflict_frequency); DISPLAY_PARAM(m_pb_learn_complements); - DISPLAY_PARAM(m_pb_enable_compilation); - DISPLAY_PARAM(m_pb_enable_simplex); -} \ No newline at end of file +} diff --git a/src/smt/params/theory_pb_params.h b/src/smt/params/theory_pb_params.h index 9bfd262c4..98c190b33 100644 --- a/src/smt/params/theory_pb_params.h +++ b/src/smt/params/theory_pb_params.h @@ -25,13 +25,9 @@ Revision History: struct theory_pb_params { unsigned m_pb_conflict_frequency; bool m_pb_learn_complements; - bool m_pb_enable_compilation; - bool m_pb_enable_simplex; theory_pb_params(params_ref const & p = params_ref()): m_pb_conflict_frequency(1000), - m_pb_learn_complements(true), - m_pb_enable_compilation(true), - m_pb_enable_simplex(false) + m_pb_learn_complements(true) {} void updt_params(params_ref const & p); diff --git a/src/smt/proto_model/array_factory.cpp b/src/smt/proto_model/array_factory.cpp index dc3e0f89c..141021006 100644 --- a/src/smt/proto_model/array_factory.cpp +++ b/src/smt/proto_model/array_factory.cpp @@ -104,7 +104,7 @@ bool array_factory::mk_two_diff_values_for(sort * s) { bool array_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { value_set * set = nullptr; - if (!m_sort2value_set.find(s, set) || set->size() == 0) { + if (!m_sort2value_set.find(s, set) || set->empty()) { if (!mk_two_diff_values_for(s)) return false; } diff --git a/src/smt/smt_arith_value.cpp b/src/smt/smt_arith_value.cpp index 443112ecc..8dfca3ebb 100644 --- a/src/smt/smt_arith_value.cpp +++ b/src/smt/smt_arith_value.cpp @@ -18,30 +18,32 @@ Revision History: --*/ #include "smt/smt_arith_value.h" -#include "smt/theory_lra.h" -#include "smt/theory_arith.h" namespace smt { - arith_value::arith_value(context& ctx): - m_ctx(ctx), m(ctx.get_manager()), a(m) {} + arith_value::arith_value(ast_manager& m): + m_ctx(nullptr), m(m), a(m) {} - bool arith_value::get_lo(expr* e, rational& lo, bool& is_strict) { - if (!m_ctx.e_internalized(e)) return false; + void arith_value::init(context* ctx) { + m_ctx = ctx; family_id afid = a.get_family_id(); + theory* th = m_ctx->get_theory(afid); + m_tha = dynamic_cast(th); + m_thi = dynamic_cast(th); + m_thr = dynamic_cast(th); + } + + bool arith_value::get_lo_equiv(expr* e, rational& lo, bool& is_strict) { + if (!m_ctx->e_internalized(e)) return false; is_strict = false; - enode* next = m_ctx.get_enode(e), *n = next; + enode* next = m_ctx->get_enode(e), *n = next; bool found = false; bool is_strict1; rational lo1; - theory* th = m_ctx.get_theory(afid); - theory_mi_arith* tha = dynamic_cast(th); - theory_i_arith* thi = dynamic_cast(th); - theory_lra* thr = dynamic_cast(th); do { - if ((tha && tha->get_lower(next, lo1, is_strict1)) || - (thi && thi->get_lower(next, lo1, is_strict1)) || - (thr && thr->get_lower(next, lo1, is_strict1))) { + if ((m_tha && m_tha->get_lower(next, lo1, is_strict1)) || + (m_thi && m_thi->get_lower(next, lo1, is_strict1)) || + (m_thr && m_thr->get_lower(next, lo1, is_strict1))) { if (!found || lo1 > lo || (lo == lo1 && is_strict1)) lo = lo1, is_strict = is_strict1; found = true; } @@ -51,21 +53,16 @@ namespace smt { return found; } - bool arith_value::get_up(expr* e, rational& up, bool& is_strict) { - if (!m_ctx.e_internalized(e)) return false; - family_id afid = a.get_family_id(); + bool arith_value::get_up_equiv(expr* e, rational& up, bool& is_strict) { + if (!m_ctx->e_internalized(e)) return false; is_strict = false; - enode* next = m_ctx.get_enode(e), *n = next; + enode* next = m_ctx->get_enode(e), *n = next; bool found = false, is_strict1; rational up1; - theory* th = m_ctx.get_theory(afid); - theory_mi_arith* tha = dynamic_cast(th); - theory_i_arith* thi = dynamic_cast(th); - theory_lra* thr = dynamic_cast(th); do { - if ((tha && tha->get_upper(next, up1, is_strict1)) || - (thi && thi->get_upper(next, up1, is_strict1)) || - (thr && thr->get_upper(next, up1, is_strict1))) { + if ((m_tha && m_tha->get_upper(next, up1, is_strict1)) || + (m_thi && m_thi->get_upper(next, up1, is_strict1)) || + (m_thr && m_thr->get_upper(next, up1, is_strict1))) { if (!found || up1 < up || (up1 == up && is_strict1)) up = up1, is_strict = is_strict1; found = true; } @@ -75,20 +72,46 @@ namespace smt { return found; } - bool arith_value::get_value(expr* e, rational& val) { - if (!m_ctx.e_internalized(e)) return false; + bool arith_value::get_up(expr* e, rational& up, bool& is_strict) const { + if (!m_ctx->e_internalized(e)) return false; + is_strict = false; + enode* n = m_ctx->get_enode(e); + if (m_tha) return m_tha->get_upper(n, up, is_strict); + if (m_thi) return m_thi->get_upper(n, up, is_strict); + if (m_thr) return m_thr->get_upper(n, up, is_strict); + return false; + } + + bool arith_value::get_lo(expr* e, rational& up, bool& is_strict) const { + if (!m_ctx->e_internalized(e)) return false; + is_strict = false; + enode* n = m_ctx->get_enode(e); + if (m_tha) return m_tha->get_lower(n, up, is_strict); + if (m_thi) return m_thi->get_lower(n, up, is_strict); + if (m_thr) return m_thr->get_lower(n, up, is_strict); + return false; + } + + bool arith_value::get_value(expr* e, rational& val) const { + if (!m_ctx->e_internalized(e)) return false; expr_ref _val(m); - enode* next = m_ctx.get_enode(e), *n = next; - family_id afid = a.get_family_id(); - theory* th = m_ctx.get_theory(afid); - theory_mi_arith* tha = dynamic_cast(th); - theory_i_arith* thi = dynamic_cast(th); - theory_lra* thr = dynamic_cast(th); + enode* n = m_ctx->get_enode(e); + if (m_tha && m_tha->get_value(n, _val) && a.is_numeral(_val, val)) return true; + if (m_thi && m_thi->get_value(n, _val) && a.is_numeral(_val, val)) return true; + if (m_thr && m_thr->get_value(n, val)) return true; + return false; + } + + + bool arith_value::get_value_equiv(expr* e, rational& val) const { + if (!m_ctx->e_internalized(e)) return false; + expr_ref _val(m); + enode* next = m_ctx->get_enode(e), *n = next; do { e = next->get_owner(); - if (tha && tha->get_value(next, _val) && a.is_numeral(_val, val)) return true; - if (thi && thi->get_value(next, _val) && a.is_numeral(_val, val)) return true; - if (thr && thr->get_value(next, val)) return true; + if (m_tha && m_tha->get_value(next, _val) && a.is_numeral(_val, val)) return true; + if (m_thi && m_thi->get_value(next, _val) && a.is_numeral(_val, val)) return true; + if (m_thr && m_thr->get_value(next, val)) return true; next = next->get_next(); } while (next != n); @@ -97,7 +120,7 @@ namespace smt { final_check_status arith_value::final_check() { family_id afid = a.get_family_id(); - theory * th = m_ctx.get_theory(afid); + theory * th = m_ctx->get_theory(afid); return th->final_check_eh(); } }; diff --git a/src/smt/smt_arith_value.h b/src/smt/smt_arith_value.h index b819b2b9a..7fa1ecc31 100644 --- a/src/smt/smt_arith_value.h +++ b/src/smt/smt_arith_value.h @@ -21,18 +21,28 @@ Revision History: #include "ast/arith_decl_plugin.h" #include "smt/smt_context.h" +#include "smt/theory_lra.h" +#include "smt/theory_arith.h" namespace smt { class arith_value { - context& m_ctx; + context* m_ctx; ast_manager& m; arith_util a; + theory_mi_arith* m_tha; + theory_i_arith* m_thi; + theory_lra* m_thr; public: - arith_value(context& ctx); - bool get_lo(expr* e, rational& lo, bool& strict); - bool get_up(expr* e, rational& up, bool& strict); - bool get_value(expr* e, rational& value); + arith_value(ast_manager& m); + void init(context* ctx); + bool get_lo_equiv(expr* e, rational& lo, bool& strict); + bool get_up_equiv(expr* e, rational& up, bool& strict); + bool get_value_equiv(expr* e, rational& value) const; + bool get_lo(expr* e, rational& lo, bool& strict) const; + bool get_up(expr* e, rational& up, bool& strict) const; + bool get_value(expr* e, rational& value) const; + bool get_fixed(expr* e, rational& value) const; final_check_status final_check(); }; }; diff --git a/src/smt/smt_case_split_queue.cpp b/src/smt/smt_case_split_queue.cpp index 5c51b906f..a4cb68506 100644 --- a/src/smt/smt_case_split_queue.cpp +++ b/src/smt/smt_case_split_queue.cpp @@ -80,6 +80,11 @@ namespace smt { m_queue.decreased(v); } + void activity_decreased_eh(bool_var v) override { + if (m_queue.contains(v)) + m_queue.increased(v); + } + void mk_var_eh(bool_var v) override { m_queue.reserve(v+1); SASSERT(!m_queue.contains(v)); @@ -167,6 +172,14 @@ namespace smt { m_delayed_queue.decreased(v); } + void activity_decreased_eh(bool_var v) override { + act_case_split_queue::activity_decreased_eh(v); + if (m_queue.contains(v)) + m_queue.increased(v); + if (m_delayed_queue.contains(v)) + m_delayed_queue.increased(v); + } + void mk_var_eh(bool_var v) override { m_queue.reserve(v+1); m_delayed_queue.reserve(v+1); @@ -281,7 +294,7 @@ namespace smt { } } if (order == 1) { - if (undef_children.size() == 0) { + if (undef_children.empty()) { // a bug? } else if (undef_children.size() == 1) { undef_child = undef_children[0]; @@ -324,6 +337,8 @@ namespace smt { void activity_increased_eh(bool_var v) override {} + void activity_decreased_eh(bool_var v) override {} + void mk_var_eh(bool_var v) override {} void del_var_eh(bool_var v) override {} @@ -509,6 +524,8 @@ namespace smt { void activity_increased_eh(bool_var v) override {} + void activity_decreased_eh(bool_var v) override {} + void mk_var_eh(bool_var v) override { if (m_context.is_searching()) { SASSERT(v >= m_bs_num_bool_vars); @@ -753,6 +770,8 @@ namespace smt { void activity_increased_eh(bool_var v) override {} + void activity_decreased_eh(bool_var v) override {} + void mk_var_eh(bool_var v) override {} void del_var_eh(bool_var v) override {} @@ -1133,6 +1152,11 @@ namespace smt { m_queue.decreased(v); } + void activity_decreased_eh(bool_var v) override { + if (m_queue.contains(v)) + m_queue.increased(v); + } + void mk_var_eh(bool_var v) override { m_queue.reserve(v+1); m_queue.insert(v); diff --git a/src/smt/smt_case_split_queue.h b/src/smt/smt_case_split_queue.h index cfa33bfe2..3bad083c8 100644 --- a/src/smt/smt_case_split_queue.h +++ b/src/smt/smt_case_split_queue.h @@ -32,6 +32,7 @@ namespace smt { class case_split_queue { public: virtual void activity_increased_eh(bool_var v) = 0; + virtual void activity_decreased_eh(bool_var v) = 0; virtual void mk_var_eh(bool_var v) = 0; virtual void del_var_eh(bool_var v) = 0; virtual void assign_lit_eh(literal l) {} diff --git a/src/smt/smt_cg_table.cpp b/src/smt/smt_cg_table.cpp index ad15fd819..b85fed02d 100644 --- a/src/smt/smt_cg_table.cpp +++ b/src/smt/smt_cg_table.cpp @@ -71,10 +71,7 @@ namespace smt { void cg_table::display(std::ostream & out) const { out << "congruence table:\n"; - table::iterator it = m_table.begin(); - table::iterator end = m_table.end(); - for (; it != end; ++it) { - enode * n = *it; + for (enode * n : m_table) { out << mk_pp(n->get_owner(), m_manager) << "\n"; } } @@ -82,10 +79,7 @@ namespace smt { void cg_table::display_compact(std::ostream & out) const { if (!m_table.empty()) { out << "congruence table:\n"; - table::iterator it = m_table.begin(); - table::iterator end = m_table.end(); - for (; it != end; ++it) { - enode * n = *it; + for (enode * n : m_table) { out << "#" << n->get_owner()->get_id() << " "; } out << "\n"; @@ -94,10 +88,7 @@ namespace smt { #ifdef Z3DEBUG bool cg_table::check_invariant() const { - table::iterator it = m_table.begin(); - table::iterator end = m_table.end(); - for (; it != end; ++it) { - enode * n = *it; + for (enode * n : m_table) { CTRACE("cg_table", !contains_ptr(n), tout << "#" << n->get_owner_id() << "\n";); SASSERT(contains_ptr(n)); } @@ -136,9 +127,11 @@ namespace smt { } bool cg_table::cg_eq::operator()(enode * n1, enode * n2) const { - SASSERT(n1->get_num_args() == n2->get_num_args()); SASSERT(n1->get_decl() == n2->get_decl()); unsigned num = n1->get_num_args(); + if (num != n2->get_num_args()) { + return false; + } for (unsigned i = 0; i < num; i++) if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root()) return false; @@ -205,10 +198,7 @@ namespace smt { } void cg_table::reset() { - ptr_vector::iterator it = m_tables.begin(); - ptr_vector::iterator end = m_tables.end(); - for (; it != end; ++it) { - void * t = *it; + for (void* t : m_tables) { switch (GET_TAG(t)) { case UNARY: dealloc(UNTAG(unary_table*, t)); @@ -225,10 +215,9 @@ namespace smt { } } m_tables.reset(); - obj_map::iterator it2 = m_func_decl2id.begin(); - obj_map::iterator end2 = m_func_decl2id.end(); - for (; it2 != end2; ++it2) - m_manager.dec_ref(it2->m_key); + for (auto const& kv : m_func_decl2id) { + m_manager.dec_ref(kv.m_key); + } m_func_decl2id.reset(); } diff --git a/src/smt/smt_cg_table.h b/src/smt/smt_cg_table.h index 64c8328d0..4085ccc5f 100644 --- a/src/smt/smt_cg_table.h +++ b/src/smt/smt_cg_table.h @@ -252,6 +252,8 @@ namespace smt { enode_bool_pair insert(enode * n) { // it doesn't make sense to insert a constant. SASSERT(n->get_num_args() > 0); + SASSERT(!m_manager.is_and(n->get_owner())); + SASSERT(!m_manager.is_or(n->get_owner())); enode * n_prime; void * t = get_table(n); switch (static_cast(GET_TAG(t))) { diff --git a/src/smt/smt_clause.cpp b/src/smt/smt_clause.cpp index 2b9b8dd3e..a1f1f9f03 100644 --- a/src/smt/smt_clause.cpp +++ b/src/smt/smt_clause.cpp @@ -19,6 +19,7 @@ Revision History: #include "smt/smt_clause.h" #include "smt/smt_justification.h" #include "ast/ast_ll_pp.h" +#include "ast/ast_pp.h" namespace smt { /** @@ -96,22 +97,33 @@ namespace smt { } } - void clause::display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const { + std::ostream& clause::display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const { out << "(clause"; for (unsigned i = 0; i < m_num_literals; i++) { out << " "; m_lits[i].display(out, m, bool_var2expr_map); } - out << ")"; + return out << ")"; } - void clause::display_compact(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const { + std::ostream& clause::display_compact(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const { out << "(clause"; for (unsigned i = 0; i < m_num_literals; i++) { out << " "; m_lits[i].display_compact(out, bool_var2expr_map); } - out << ")"; + return out << ")"; + } + + std::ostream& clause::display_smt2(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const { + expr_ref_vector args(m); + for (unsigned i = 0; i < m_num_literals; i++) { + literal lit = m_lits[i]; + args.push_back(bool_var2expr_map[lit.var()]); + if (lit.sign()) args[args.size()-1] = m.mk_not(args.back()); + } + expr_ref disj(m.mk_or(args.size(), args.c_ptr()), m); + return out << disj; } }; diff --git a/src/smt/smt_clause.h b/src/smt/smt_clause.h index f0b352e05..025e2389d 100644 --- a/src/smt/smt_clause.h +++ b/src/smt/smt_clause.h @@ -239,9 +239,11 @@ namespace smt { set_activity(get_activity() + 1); } - void display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const; + std::ostream& display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const; + + std::ostream& display_smt2(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const; - void display_compact(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const; + std::ostream& display_compact(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const; unsigned hash() const { return get_ptr_hash(this); diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index 93c172bf1..9a3ae7728 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -311,7 +311,7 @@ namespace smt { bool_var var = antecedent.var(); unsigned lvl = m_ctx.get_assign_level(var); SASSERT(var < static_cast(m_ctx.get_num_bool_vars())); - TRACE("conflict", tout << "processing antecedent (level " << lvl << "):"; + TRACE("conflict_", tout << "processing antecedent (level " << lvl << "):"; m_ctx.display_literal(tout, antecedent); m_ctx.display_detailed_literal(tout << " ", antecedent) << "\n";); @@ -429,7 +429,7 @@ namespace smt { void conflict_resolution::finalize_resolve(b_justification conflict, literal not_l) { unmark_justifications(0); - TRACE("conflict", m_ctx.display_literals(tout << "before minimization:\n", m_lemma) << "\n";); + TRACE("conflict", m_ctx.display_literals(tout << "before minimization:\n", m_lemma) << "\n" << m_lemma << "\n";); TRACE("conflict_verbose",m_ctx.display_literals_verbose(tout << "before minimization:\n", m_lemma) << "\n";); @@ -484,7 +484,7 @@ namespace smt { unsigned num_marks = 0; if (not_l != null_literal) { - TRACE("conflict", tout << "not_l: "; m_ctx.display_literal_verbose(tout, not_l); tout << "\n";); + TRACE("conflict", tout << "not_l: "; m_ctx.display_literal_verbose(tout, not_l) << "\n";); process_antecedent(not_l, num_marks); } @@ -497,7 +497,7 @@ namespace smt { } TRACE("conflict", tout << "processing consequent id: " << idx << " lit: " << consequent << " "; m_ctx.display_literal(tout, consequent); - m_ctx.display_literal_verbose(tout, consequent) << "\n"; + m_ctx.display_literal_verbose(tout << " ", consequent) << "\n"; tout << "num_marks: " << num_marks << ", js kind: " << js.get_kind() << " level: " << m_ctx.get_assign_level(consequent) << "\n"; ); SASSERT(js != null_b_justification); @@ -1372,7 +1372,7 @@ namespace smt { } while (true) { - TRACE("unsat_core_bug", tout << consequent << " js.get_kind(): " << js.get_kind() << ", idx: " << idx << "\n";); + TRACE("unsat_core_bug", tout << consequent << ", idx: " << idx << " " << js.get_kind() << "\n";); switch (js.get_kind()) { case b_justification::CLAUSE: { clause * cls = js.get_clause(); @@ -1417,7 +1417,7 @@ namespace smt { } while (idx >= 0) { literal l = m_assigned_literals[idx]; - TRACE("unsat_core_bug", tout << "l: " << l << ", get_assign_level(l): " << m_ctx.get_assign_level(l) << ", is_marked(l): " << m_ctx.is_marked(l.var()) << "\n";); + CTRACE("unsat_core_bug", m_ctx.is_marked(l.var()), tout << "l: " << l << ", get_assign_level(l): " << m_ctx.get_assign_level(l) << "\n";); if (m_ctx.get_assign_level(l) < search_lvl) goto end_unsat_core; if (m_ctx.is_marked(l.var())) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 7e7ed14b5..6b15501ea 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1776,7 +1776,7 @@ namespace smt { void context::set_conflict(const b_justification & js, literal not_l) { if (!inconsistent()) { - TRACE("set_conflict", display_literal_verbose(tout, not_l); display(tout << " ", js); ); + TRACE("set_conflict", display_literal_verbose(tout << m_scope_lvl << " ", not_l); display(tout << " ", js); ); m_conflict = js; m_not_l = not_l; } @@ -1815,7 +1815,7 @@ namespace smt { } /** - \brief Execute next clase split, return false if there are no + \brief Execute next case split, return false if there are no more case splits to be performed. */ bool context::decide() { @@ -1860,7 +1860,7 @@ namespace smt { if (is_quantifier(m_bool_var2expr[var])) { // Overriding any decision on how to assign the quantifier. - // assigining a quantifier to false is equivalent to make it irrelevant. + // assigning a quantifier to false is equivalent to make it irrelevant. phase = l_false; } @@ -2136,7 +2136,7 @@ namespace smt { \brief When a clause is reinitialized (see reinit_clauses) enodes and literals may need to be recreated. When an enode is recreated, I want to use the same generation number it had before being deleted. Otherwise the generation will be 0, and will affect - the loop prevetion heuristics used to control quantifier instantiation. + the loop prevention heuristics used to control quantifier instantiation. Thus, I cache the generation number of enodes that will be deleted during backtracking and recreated by reinit_clauses. */ @@ -3246,8 +3246,13 @@ namespace smt { proof * pr = m_manager.mk_asserted(curr_assumption); internalize_assertion(curr_assumption, pr, 0); literal l = get_literal(curr_assumption); + if (l == true_literal) + continue; + if (l == false_literal) { + set_conflict(b_justification::mk_axiom()); + break; + } m_literal2assumption.insert(l.index(), orig_assumption); - // mark_as_relevant(l); <<< not needed // internalize_assertion marked l as relevant. SASSERT(is_relevant(l)); TRACE("assumptions", tout << l << ":" << curr_assumption << " " << mk_pp(orig_assumption, m_manager) << "\n";); @@ -3493,7 +3498,6 @@ namespace smt { m_case_split_queue ->init_search_eh(); m_next_progress_sample = 0; TRACE("literal_occ", display_literal_num_occs(tout);); - m_timer.start(); } void context::end_search() { @@ -3874,7 +3878,7 @@ namespace smt { for (unsigned i = head; i < sz; i++) { literal l = m_assigned_literals[i]; bool_var v = l.var(); - TRACE("forget_phase", tout << "forgeting phase of l: " << l << "\n";); + TRACE("forget_phase", tout << "forgetting phase of l: " << l << "\n";); m_bdata[v].m_phase_available = false; } } @@ -3920,6 +3924,7 @@ namespace smt { conflict_lvl > m_search_lvl + 1 && !m_manager.proofs_enabled() && m_units_to_reassert.size() < m_fparams.m_delay_units_threshold; + if (delay_forced_restart) { new_lvl = conflict_lvl - 1; } @@ -4419,6 +4424,22 @@ namespace smt { m = const_cast(m_model.get()); } + void context::get_levels(ptr_vector const& vars, unsigned_vector& depth) { + unsigned sz = vars.size(); + depth.resize(sz); + for (unsigned i = 0; i < sz; ++i) { + expr* v = vars[i]; + bool_var bv = m_expr2bool_var.get(v->get_id(), null_bool_var); + depth[i] = bv == null_bool_var ? UINT_MAX : get_assign_level(bv); + } + } + + expr_ref_vector context::get_trail() { + expr_ref_vector result(get_manager()); + get_assignments(result); + return result; + } + void context::get_proto_model(proto_model_ref & m) const { m = const_cast(m_proto_model.get()); } diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 7657795bd..d4d4ec6fb 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -414,10 +414,19 @@ namespace smt { return m_activity[v]; } - void set_activity(bool_var v, double & act) { + void set_activity(bool_var v, double const & act) { m_activity[v] = act; } + void activity_changed(bool_var v, bool increased) { + if (increased) { + m_case_split_queue->activity_increased_eh(v); + } + else { + m_case_split_queue->activity_decreased_eh(v); + } + } + bool is_assumption(bool_var v) const { return get_bdata(v).m_assumption; } @@ -1360,7 +1369,7 @@ namespace smt { void display_profile(std::ostream & out) const; - void display(std::ostream& out, b_justification j) const; + std::ostream& display(std::ostream& out, b_justification j) const; // ----------------------------------- // @@ -1577,6 +1586,10 @@ namespace smt { return m_unsat_core.get(idx); } + void get_levels(ptr_vector const& vars, unsigned_vector& depth); + + expr_ref_vector get_trail(); + void get_model(model_ref & m) const; bool update_model(bool refinalize); diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 5dea80f5e..c40b829a7 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -160,7 +160,7 @@ namespace smt { } void context::display_clause(std::ostream & out, clause const * cls) const { - cls->display_compact(out, m_manager, m_bool_var2expr.c_ptr()); + cls->display_smt2(out, m_manager, m_bool_var2expr.c_ptr()); } void context::display_clauses(std::ostream & out, ptr_vector const & v) const { @@ -185,11 +185,18 @@ namespace smt { out << "binary clauses:\n"; first = false; } + expr_ref t1(m_manager), t2(m_manager); + literal2expr(neg_l1, t1); + literal2expr(l2, t2); + expr_ref disj(m_manager.mk_or(t1, t2), m_manager); + out << disj << "\n"; +#if 0 out << "(clause "; display_literal(out, neg_l1); out << " "; display_literal(out, l2); out << ")\n"; +#endif } } } @@ -200,6 +207,7 @@ namespace smt { out << "current assignment:\n"; for (literal lit : m_assigned_literals) { display_literal(out, lit); + if (!is_relevant(lit)) out << " n "; out << ": "; display_verbose(out, m_manager, 1, &lit, m_bool_var2expr.c_ptr()); out << "\n"; @@ -255,7 +263,7 @@ namespace smt { for (unsigned i = 0; i < sz; i++) { expr * n = m_b_internalized_stack.get(i); bool_var v = get_bool_var_of_id(n->get_id()); - out << "(#" << n->get_id() << " -> p!" << v << ") "; + out << "(#" << n->get_id() << " -> " << literal(v, false) << ") "; } out << "\n"; } @@ -348,9 +356,9 @@ namespace smt { } void context::display_unsat_core(std::ostream & out) const { - unsigned sz = m_unsat_core.size(); - for (unsigned i = 0; i < sz; i++) - out << mk_pp(m_unsat_core.get(i), m_manager) << "\n"; + for (expr* c : m_unsat_core) { + out << mk_pp(c, m_manager) << "\n"; + } } void context::collect_statistics(::statistics & st) const { @@ -555,23 +563,21 @@ namespace smt { } out << "\n"; if (is_app(n)) { - for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) - todo.push_back(to_app(n)->get_arg(i)); + for (expr* arg : *to_app(n)) { + todo.push_back(arg); + } } } } - void context::display(std::ostream& out, b_justification j) const { + std::ostream& context::display(std::ostream& out, b_justification j) const { switch (j.get_kind()) { case b_justification::AXIOM: out << "axiom"; break; - case b_justification::BIN_CLAUSE: { - literal l2 = j.get_literal(); - out << "bin-clause "; - display_literal(out, l2); + case b_justification::BIN_CLAUSE: + out << "bin " << j.get_literal(); break; - } case b_justification::CLAUSE: { clause * cls = j.get_clause(); out << "clause "; @@ -579,17 +585,16 @@ namespace smt { break; } case b_justification::JUSTIFICATION: { - out << "justification " << j.get_justification()->get_from_theory() << ": "; literal_vector lits; const_cast(*m_conflict_resolution).justification2literals(j.get_justification(), lits); - display_literals(out, lits); + out << "justification " << j.get_justification()->get_from_theory() << ": " << lits; break; } default: UNREACHABLE(); break; } - out << "\n"; + return out << "\n"; } void context::trace_assign(literal l, b_justification j, bool decision) const { diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 5f9e031e5..eb2673da9 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -196,7 +196,7 @@ namespace smt { TRACE("incompleteness_bug", tout << "[internalize-assertion]: #" << n->get_id() << "\n";); flet l(m_generation, generation); m_stats.m_max_generation = std::max(m_generation, m_stats.m_max_generation); - if (get_depth(n) > DEEP_EXPR_THRESHOLD) { + if (::get_depth(n) > DEEP_EXPR_THRESHOLD) { // if the expression is deep, then execute topological sort to avoid // stack overflow. // a caveat is that theory internalizers do rely on recursive descent so diff --git a/src/smt/smt_kernel.cpp b/src/smt/smt_kernel.cpp index b03604b5b..adcda3979 100644 --- a/src/smt/smt_kernel.cpp +++ b/src/smt/smt_kernel.cpp @@ -146,6 +146,29 @@ namespace smt { expr * get_unsat_core_expr(unsigned idx) const { return m_kernel.get_unsat_core_expr(idx); } + + void get_levels(ptr_vector const& vars, unsigned_vector& depth) { + m_kernel.get_levels(vars, depth); + } + + expr_ref_vector get_trail() { + return m_kernel.get_trail(); + } + + void set_activity(expr* lit, double act) { + SASSERT(m().is_bool(lit)); + m().is_not(lit, lit); + if (!m_kernel.b_internalized(lit)) { + m_kernel.internalize(lit, false); + } + if (!m_kernel.b_internalized(lit)) { + return; + } + auto v = m_kernel.get_bool_var(lit); + double old_act = m_kernel.get_activity(v); + m_kernel.set_activity(v, act); + m_kernel.activity_changed(v, act > old_act); + } failure last_failure() const { return m_kernel.get_last_search_failure(); @@ -396,4 +419,17 @@ namespace smt { return m_imp->m_kernel; } + void kernel::get_levels(ptr_vector const& vars, unsigned_vector& depth) { + m_imp->get_levels(vars, depth); + } + + expr_ref_vector kernel::get_trail() { + return m_imp->get_trail(); + } + + void kernel::set_activity(expr* lit, double activity) { + m_imp->set_activity(lit, activity); + } + + }; diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index d78f71e20..a46195e02 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -219,6 +219,21 @@ namespace smt { */ expr* next_decision(); + /** + \brief retrieve depth of variables from decision stack. + */ + void get_levels(ptr_vector const& vars, unsigned_vector& depth); + + /** + \brief retrieve trail of assignment stack. + */ + expr_ref_vector get_trail(); + + /** + \brief set activity of literal + */ + void set_activity(expr* lit, double activity); + /** \brief (For debubbing purposes) Prints the state of the kernel */ diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index 30ea7b18e..111b9a0c0 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -1703,7 +1703,7 @@ namespace smt { friend class quantifier_analyzer; void checkpoint() { - m_mf.checkpoint("quantifer_info"); + m_mf.checkpoint("quantifier_info"); } void insert_qinfo(qinfo * qi) { diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp index 01a9353f7..cb7d12d2e 100644 --- a/src/smt/smt_quantifier.cpp +++ b/src/smt/smt_quantifier.cpp @@ -17,7 +17,7 @@ Revision History: --*/ #include "ast/ast_pp.h" -#include "ast/ast_smt2_pp.h" +#include "ast/ast_ll_pp.h" #include "smt/smt_quantifier.h" #include "smt/smt_context.h" #include "smt/smt_quantifier_stat.h" @@ -36,14 +36,14 @@ namespace smt { \brief Ensures that all relevant proof steps to explain why the enode is equal to the root of its equivalence class are in the log and up-to-date. */ - void quantifier_manager::log_justification_to_root(std::ostream & log, enode *en, obj_hashtable &already_visited, context &ctx, ast_manager &m) { + void quantifier_manager::log_justification_to_root(std::ostream & out, enode *en, obj_hashtable &visited, context &ctx, ast_manager &m) { enode *root = en->get_root(); for (enode *it = en; it != root; it = it->get_trans_justification().m_target) { - if (already_visited.find(it) == already_visited.end()) already_visited.insert(it); + if (visited.find(it) == visited.end()) visited.insert(it); else break; if (!it->m_proof_is_logged) { - log_single_justification(log, it, already_visited, ctx, m); + log_single_justification(out, it, visited, ctx, m); it->m_proof_is_logged = true; } else if (it->get_trans_justification().m_justification.get_kind() == smt::eq_justification::kind::CONGRUENCE) { @@ -52,14 +52,14 @@ namespace smt { enode *target = it->get_trans_justification().m_target; for (unsigned i = 0; i < num_args; ++i) { - log_justification_to_root(log, it->get_arg(i), already_visited, ctx, m); - log_justification_to_root(log, target->get_arg(i), already_visited, ctx, m); + log_justification_to_root(out, it->get_arg(i), visited, ctx, m); + log_justification_to_root(out, target->get_arg(i), visited, ctx, m); } it->m_proof_is_logged = true; } } if (!root->m_proof_is_logged) { - log << "[eq-expl] #" << root->get_owner_id() << " root\n"; + out << "[eq-expl] #" << root->get_owner_id() << " root\n"; root->m_proof_is_logged = true; } } @@ -68,7 +68,7 @@ namespace smt { \brief Logs a single equality explanation step and, if necessary, recursively calls log_justification_to_root to log equalities needed by the step (e.g. argument equalities for congruence steps). */ - void quantifier_manager::log_single_justification(std::ostream & out, enode *en, obj_hashtable &already_visited, context &ctx, ast_manager &m) { + void quantifier_manager::log_single_justification(std::ostream & out, enode *en, obj_hashtable &visited, context &ctx, ast_manager &m) { smt::literal lit; unsigned num_args; enode *target = en->get_trans_justification().m_target; @@ -87,8 +87,8 @@ namespace smt { num_args = en->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { - log_justification_to_root(out, en->get_arg(i), already_visited, ctx, m); - log_justification_to_root(out, target->get_arg(i), already_visited, ctx, m); + log_justification_to_root(out, en->get_arg(i), visited, ctx, m); + log_justification_to_root(out, target->get_arg(i), visited, ctx, m); } out << "[eq-expl] #" << en->get_owner_id() << " cg"; @@ -195,6 +195,60 @@ namespace smt { return m_plugin->is_shared(n); } + void log_add_instance( + fingerprint* f, + quantifier * q, app * pat, + unsigned num_bindings, + enode * const * bindings, + vector> & used_enodes) { + + if (pat == nullptr) { + trace_stream() << "[inst-discovered] MBQI " << static_cast(f) << " #" << q->get_id(); + for (unsigned i = 0; i < num_bindings; ++i) { + trace_stream() << " #" << bindings[num_bindings - i - 1]->get_owner_id(); + } + trace_stream() << "\n"; + } else { + std::ostream & out = trace_stream(); + + obj_hashtable already_visited; + + // In the term produced by the quantifier instantiation the root of the equivalence class of the terms bound to the quantified variables + // is used. We need to make sure that all of these equalities appear in the log. + for (unsigned i = 0; i < num_bindings; ++i) { + log_justification_to_root(out, bindings[i], already_visited, m_context, m()); + } + + for (auto n : used_enodes) { + enode *orig = std::get<0>(n); + enode *substituted = std::get<1>(n); + if (orig != nullptr) { + log_justification_to_root(out, orig, already_visited, m_context, m()); + log_justification_to_root(out, substituted, already_visited, m_context, m()); + } + } + + // At this point all relevant equalities for the match are logged. + out << "[new-match] " << static_cast(f) << " #" << q->get_id() << " #" << pat->get_id(); + for (unsigned i = 0; i < num_bindings; i++) { + // I don't want to use mk_pp because it creates expressions for pretty printing. + // This nasty side-effect may change the behavior of Z3. + out << " #" << bindings[num_bindings - i - 1]->get_owner_id(); + } + out << " ;"; + for (auto n : used_enodes) { + enode *orig = std::get<0>(n); + enode *substituted = std::get<1>(n); + if (orig == nullptr) + out << " #" << substituted->get_owner_id(); + else { + out << " (#" << orig->get_owner_id() << " #" << substituted->get_owner_id() << ")"; + } + } + out << "\n"; + } + } + bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, @@ -211,62 +265,18 @@ namespace smt { fingerprint * f = m_context.add_fingerprint(q, q->get_id(), num_bindings, bindings, def); if (f) { if (has_trace_stream()) { - if (pat == nullptr) { - trace_stream() << "[inst-discovered] MBQI " << static_cast(f) << " #" << q->get_id(); - for (unsigned i = 0; i < num_bindings; ++i) { - trace_stream() << " #" << bindings[num_bindings - i - 1]->get_owner_id(); - } - trace_stream() << "\n"; - } else { - std::ostream & out = trace_stream(); - - obj_hashtable already_visited; - - // In the term produced by the quantifier instantiation the root of the equivalence class of the terms bound to the quantified variables - // is used. We need to make sure that all of these equalities appear in the log. - for (unsigned i = 0; i < num_bindings; ++i) { - log_justification_to_root(out, bindings[i], already_visited, m_context, m()); - } - - for (auto n : used_enodes) { - enode *orig = std::get<0>(n); - enode *substituted = std::get<1>(n); - if (orig != nullptr) { - log_justification_to_root(out, orig, already_visited, m_context, m()); - log_justification_to_root(out, substituted, already_visited, m_context, m()); - } - } - - // At this point all relevant equalities for the match are logged. - out << "[new-match] " << static_cast(f) << " #" << q->get_id() << " #" << pat->get_id(); - for (unsigned i = 0; i < num_bindings; i++) { - // I don't want to use mk_pp because it creates expressions for pretty printing. - // This nasty side-effect may change the behavior of Z3. - out << " #" << bindings[num_bindings - i - 1]->get_owner_id(); - } - out << " ;"; - for (auto n : used_enodes) { - enode *orig = std::get<0>(n); - enode *substituted = std::get<1>(n); - if (orig == nullptr) - out << " #" << substituted->get_owner_id(); - else { - out << " (#" << orig->get_owner_id() << " #" << substituted->get_owner_id() << ")"; - } - } - out << "\n"; - } + log_add_instance(f, q, pat, num_bindings, bindings, used_enodes); } m_qi_queue.insert(f, pat, max_generation, min_top_generation, max_top_generation); // TODO m_num_instances++; } - TRACE("quantifier", - tout << mk_pp(q, m()) << " "; + + CTRACE("quantifier_", f != nullptr, + tout << expr_ref(q, m()) << " "; for (unsigned i = 0; i < num_bindings; ++i) { - tout << mk_pp(bindings[i]->get_owner(), m()) << " "; + tout << expr_ref(bindings[i]->get_owner(), m()) << " "; } tout << "\n"; - tout << "inserted: " << (f != 0) << "\n"; ); return f != nullptr; @@ -646,7 +656,7 @@ namespace smt { m_lazy_mam->add_pattern(q, mp); } else { - TRACE("quantifier", tout << "adding:\n" << mk_ismt2_pp(mp, m) << "\n";); + TRACE("quantifier", tout << "adding:\n" << expr_ref(mp, m) << "\n";); m_mam->add_pattern(q, mp); } if (!unary) diff --git a/src/smt/smt_relevancy.cpp b/src/smt/smt_relevancy.cpp index 7db5110ee..75c82fa89 100644 --- a/src/smt/smt_relevancy.cpp +++ b/src/smt/smt_relevancy.cpp @@ -129,7 +129,7 @@ namespace smt { struct relevancy_propagator_imp : public relevancy_propagator { unsigned m_qhead; expr_ref_vector m_relevant_exprs; - obj_hashtable m_is_relevant; + uint_set m_is_relevant; typedef list relevancy_ehs; obj_map m_relevant_ehs; obj_map m_watches[2]; @@ -242,7 +242,7 @@ namespace smt { } } - bool is_relevant_core(expr * n) const { return m_is_relevant.contains(n); } + bool is_relevant_core(expr * n) const { return m_is_relevant.contains(n->get_id()); } bool is_relevant(expr * n) const override { return !enabled() || is_relevant_core(n); @@ -275,7 +275,7 @@ namespace smt { while (i != old_lim) { --i; expr * n = m_relevant_exprs.get(i); - m_is_relevant.erase(n); + m_is_relevant.remove(n->get_id()); TRACE("propagate_relevancy", tout << "unmarking:\n" << mk_ismt2_pp(n, get_manager()) << "\n";); } m_relevant_exprs.shrink(old_lim); @@ -303,7 +303,7 @@ namespace smt { } void set_relevant(expr * n) { - m_is_relevant.insert(n); + m_is_relevant.insert(n->get_id()); m_relevant_exprs.push_back(n); m_context.relevant_eh(n); } diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 2cf0e2651..a6b27784f 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -209,7 +209,7 @@ namespace smt { static void check_no_arithmetic(static_features const & st, char const * logic) { if (st.m_num_arith_ineqs > 0 || st.m_num_arith_terms > 0 || st.m_num_arith_eqs > 0) - throw default_exception("Benchmark constains arithmetic, but specified logic does not support it."); + throw default_exception("Benchmark constrains arithmetic, but specified logic does not support it."); } void setup::setup_QF_UF() { diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 48f6053fc..0052dd316 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -185,7 +185,7 @@ namespace smt { m_context.pop(n); } - lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { + lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) override { TRACE("solver_na2as", tout << "smt_solver::check_sat_core: " << num_assumptions << "\n";); return m_context.check(num_assumptions, assumptions); } @@ -195,6 +195,18 @@ namespace smt { return m_context.check(cube, clauses); } + void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { + m_context.get_levels(vars, depth); + } + + expr_ref_vector get_trail() override { + return m_context.get_trail(); + } + + void set_activity(expr* lit, double activity) override { + m_context.set_activity(lit, activity); + } + struct scoped_minimize_core { smt_solver& s; expr_ref_vector m_assumptions; diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index bac82b083..cf073c039 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -548,7 +548,7 @@ namespace smt { if (!it->is_dead()) { row const & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); - if (is_quasi_base(s) && m_var_occs[s].size() == 0) + if (is_quasi_base(s) && m_var_occs[s].empty()) continue; if (is_int(v)) { numeral const & c = r[it->m_row_idx].m_coeff; @@ -574,7 +574,7 @@ namespace smt { TRACE("move_unconstrained_to_base", tout << "before...\n"; display(tout);); int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { - if (m_var_occs[v].size() == 0 && is_free(v)) { + if (m_var_occs[v].empty() && is_free(v)) { switch (get_var_kind(v)) { case QUASI_BASE: break; diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 72bb263d0..283085ae3 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -485,12 +485,12 @@ namespace smt { template void theory_arith::mk_idiv_mod_axioms(expr * dividend, expr * divisor) { + th_rewriter & s = get_context().get_rewriter(); if (!m_util.is_zero(divisor)) { ast_manager & m = get_manager(); // if divisor is zero, then idiv and mod are uninterpreted functions. expr_ref div(m), mod(m), zero(m), abs_divisor(m), one(m); expr_ref eqz(m), eq(m), lower(m), upper(m); - th_rewriter & s = get_context().get_rewriter(); div = m_util.mk_idiv(dividend, divisor); mod = m_util.mk_mod(dividend, divisor); zero = m_util.mk_int(0); @@ -511,6 +511,17 @@ namespace smt { mk_axiom(eqz, upper, !m_util.is_numeral(abs_divisor)); rational k; context& ctx = get_context(); + + if (!m_util.is_numeral(divisor)) { + // (=> (> y 0) (<= (* y (div x y)) x)) + // (=> (< y 0) ???) + expr_ref div_ge(m), div_non_pos(m); + div_ge = m_util.mk_ge(m_util.mk_sub(dividend, m_util.mk_mul(divisor, div)), zero); + s(div_ge); + div_non_pos = m_util.mk_le(divisor, zero); + mk_axiom(div_non_pos, div_ge, false); + } + (void)ctx; if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) && k.is_pos() && k < rational(8)) { @@ -547,6 +558,7 @@ namespace smt { } #endif } + #if 0 // e-matching is too restrictive for multiplication. // also suffers from use-after free so formulas have to be pinned in solver. diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index 20aba9436..3931f06fb 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -65,10 +65,14 @@ namespace smt { bool result = false; var_data * d = m_var_data[v]; var_data_full * d_full = m_var_data_full[v]; - for (enode* pm : d_full->m_parent_maps) - for (enode* ps : d->m_parent_selects) + for (unsigned i = 0; i < d_full->m_parent_maps.size(); ++i) { + enode* pm = d_full->m_parent_maps[i]; + for (unsigned j = 0; j < d->m_parent_selects.size(); ++j) { + enode* ps = d->m_parent_selects[j]; if (instantiate_select_map_axiom(ps, pm)) - result = true; + result = true; + } + } return result; } @@ -137,7 +141,7 @@ namespace smt { } void theory_array_full::set_prop_upward(theory_var v, var_data* d) { - if (m_params.m_array_always_prop_upward || d->m_stores.size() >= 1) { + if (m_params.m_array_always_prop_upward || !d->m_stores.empty()) { theory_array::set_prop_upward(v, d); } else { diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 36cddb1cc..0301a86e1 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -20,9 +20,11 @@ Revision History: #include "smt/theory_bv.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" +#include "ast/bv_decl_plugin.h" #include "smt/smt_model_generator.h" #include "util/stats.h" +#define WATCH_DISEQ 0 namespace smt { @@ -52,6 +54,7 @@ namespace smt { unsigned bv_size = get_bv_size(n); context & ctx = get_context(); literal_vector & bits = m_bits[v]; + TRACE("bv", tout << "v" << v << "\n";); bits.reset(); for (unsigned i = 0; i < bv_size; i++) { app * bit = mk_bit2bool(owner, i); @@ -76,6 +79,7 @@ namespace smt { context & ctx = get_context(); SASSERT(!ctx.b_internalized(n)); + TRACE("bv", tout << "bit2bool: " << mk_pp(n, ctx.get_manager()) << "\n";); expr* first_arg = n->get_arg(0); if (!ctx.e_internalized(first_arg)) { @@ -93,23 +97,30 @@ namespace smt { // This will also force the creation of all bits for x. enode * first_arg_enode = ctx.get_enode(first_arg); get_var(first_arg_enode); +#if 0 + // constant axiomatization moved to catch all case in the end of function. + // numerals are not blasted into bit2bool, so we do this directly. rational val; unsigned sz; if (!ctx.b_internalized(n) && m_util.is_numeral(first_arg, val, sz)) { + + TRACE("bv", tout << "bit2bool constants\n";); theory_var v = first_arg_enode->get_th_var(get_id()); app* owner = first_arg_enode->get_owner(); for (unsigned i = 0; i < sz; ++i) { - ctx.internalize(mk_bit2bool(owner, i), true); + app* e = mk_bit2bool(owner, i); + ctx.internalize(e, true); } m_bits[v].reset(); rational bit; for (unsigned i = 0; i < sz; ++i) { div(val, rational::power_of_two(i), bit); mod(bit, rational(2), bit); - m_bits[v].push_back(bit.is_zero()?false_literal:true_literal); + m_bits[v].push_back(bit.is_zero()?false_literal:true_literal); } } +#endif } enode * arg = ctx.get_enode(first_arg); @@ -134,6 +145,19 @@ namespace smt { SASSERT(a->m_occs == 0); a->m_occs = new (get_region()) var_pos_occ(v_arg, idx); } + // axiomatize bit2bool on constants. + rational val; + unsigned sz; + if (m_util.is_numeral(first_arg, val, sz)) { + rational bit; + unsigned idx = n->get_decl()->get_parameter(0).get_int(); + div(val, rational::power_of_two(idx), bit); + mod(bit, rational(2), bit); + literal lit = ctx.get_literal(n); + if (bit.is_zero()) lit.neg(); + ctx.mark_as_relevant(lit); + ctx.mk_th_axiom(get_id(), 1, &lit); + } } void theory_bv::process_args(app * n) { @@ -422,16 +446,21 @@ namespace smt { }; void theory_bv::add_fixed_eq(theory_var v1, theory_var v2) { - ++m_stats.m_num_eq_dynamic; if (v1 > v2) { std::swap(v1, v2); } - unsigned sz = get_bv_size(v1); + + unsigned act = m_eq_activity[hash_u_u(v1, v2) & 0xFF]++; + if ((act & 0xFF) != 0xFF) { + return; + } + ++m_stats.m_num_eq_dynamic; ast_manager& m = get_manager(); context & ctx = get_context(); app* o1 = get_enode(v1)->get_owner(); app* o2 = get_enode(v2)->get_owner(); literal oeq = mk_eq(o1, o2, true); + unsigned sz = get_bv_size(v1); TRACE("bv", tout << mk_pp(o1, m) << " = " << mk_pp(o2, m) << " " << ctx.get_scope_level() << "\n";); @@ -606,8 +635,8 @@ namespace smt { num *= numeral(2); } expr_ref sum(m_autil.mk_add(sz, args.c_ptr()), m); - arith_rewriter arw(m); - ctx.get_rewriter()(sum); + th_rewriter rw(m); + rw(sum); literal l(mk_eq(n, sum, false)); TRACE("bv", tout << mk_pp(n, m) << "\n"; @@ -628,7 +657,9 @@ namespace smt { context& ctx = get_context(); process_args(n); mk_enode(n); - mk_bits(ctx.get_enode(n)->get_th_var(get_id())); + theory_var v = ctx.get_enode(n)->get_th_var(get_id()); + mk_bits(v); + if (!ctx.relevancy()) { assert_int2bv_axiom(n); } @@ -1094,7 +1125,6 @@ namespace smt { for (unsigned i = 0; i <= num_args; i++) { expr* arg = (i == num_args)?n:n->get_arg(i); sort* s = get_manager().get_sort(arg); - s = get_manager().get_sort(arg); if (m_util.is_bv_sort(s) && m_util.get_bv_size(arg) > m_params.m_bv_blast_max_size) { if (!m_approximates_large_bvs) { TRACE("bv", tout << "found large size bit-vector:\n" << mk_pp(n, get_manager()) << "\n";); @@ -1133,14 +1163,9 @@ namespace smt { SASSERT(get_bv_size(v1) == get_bv_size(v2)); context & ctx = get_context(); ast_manager & m = get_manager(); -#ifdef _TRACE - unsigned num_bool_vars = ctx.get_num_bool_vars(); -#endif - literal_vector & lits = m_tmp_literals; - ptr_vector exprs; - lits.reset(); - literal eq = mk_eq(get_enode(v1)->get_owner(), get_enode(v2)->get_owner(), true); - lits.push_back(eq); + if (v1 > v2) { + std::swap(v1, v2); + } literal_vector const & bits1 = m_bits[v1]; literal_vector::const_iterator it1 = bits1.begin(); literal_vector::const_iterator end1 = bits1.end(); @@ -1148,8 +1173,48 @@ namespace smt { literal_vector::const_iterator it2 = bits2.begin(); for (; it1 != end1; ++it1, ++it2) { if (*it1 == ~(*it2)) - return; // static diseq + return; + lbool val1 = ctx.get_assignment(*it1); + lbool val2 = ctx.get_assignment(*it2); + if (val1 != l_undef && val2 != l_undef && val1 != val2) { + return; + } } + +#if WATCH_DISEQ + bool_var watch_var = null_bool_var; + it1 = bits1.begin(); + it2 = bits2.begin(); + unsigned h = hash_u_u(v1, v2); + unsigned act = m_diseq_activity[hash_u_u(v1, v2) & 0xFF]++; + + for (; it1 != end1 && ((act & 0x3) != 0x3); ++it1, ++it2) { + lbool val1 = ctx.get_assignment(*it1); + lbool val2 = ctx.get_assignment(*it2); + + if (val1 == l_undef) { + watch_var = it1->var(); + } + else if (val2 == l_undef) { + watch_var = it2->var(); + } + else { + continue; + } + + m_diseq_watch.reserve(watch_var+1); + m_diseq_watch[watch_var].push_back(std::make_pair(v1, v2)); + m_diseq_watch_trail.push_back(watch_var); + return; + //m_replay_diseq.push_back(std::make_pair(v1, v2)); + } +#endif + + literal_vector & lits = m_tmp_literals; + ptr_vector exprs; + lits.reset(); + literal eq = mk_eq(get_enode(v1)->get_owner(), get_enode(v2)->get_owner(), true); + lits.push_back(eq); it1 = bits1.begin(); it2 = bits2.begin(); for (; it1 != end1; ++it1, ++it2) { @@ -1166,14 +1231,6 @@ namespace smt { if (m.has_trace_stream()) log_axiom_instantiation(m.mk_implies(m.mk_not(ctx.bool_var2expr(eq.var())), m.mk_or(exprs.size(), exprs.c_ptr()))); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; - TRACE_CODE({ - static unsigned num = 0; - static unsigned new_bool_vars = 0; - new_bool_vars += (ctx.get_num_bool_vars() - num_bool_vars); - if (num % 1000 == 0) - TRACE("expand_diseq", tout << "num: " << num << " " << new_bool_vars << "\n";); - num++; - }); } void theory_bv::assign_eh(bool_var v, bool is_true) { @@ -1195,8 +1252,19 @@ namespace smt { m_prop_queue.push_back(var_pos(curr->m_var, curr->m_idx)); curr = curr->m_next; } - TRACE("bv", tout << m_prop_queue.size() << "\n";); + TRACE("bv", tout << "prop queue size: " << m_prop_queue.size() << "\n";); propagate_bits(); + +#if WATCH_DISEQ + if (!get_context().inconsistent() && m_diseq_watch.size() > static_cast(v)) { + unsigned sz = m_diseq_watch[v].size(); + for (unsigned i = 0; i < sz; ++i) { + auto const & p = m_diseq_watch[v][i]; + expand_diseq(p.first, p.second); + } + m_diseq_watch[v].reset(); + } +#endif } } @@ -1212,7 +1280,7 @@ namespace smt { literal_vector & bits = m_bits[v]; literal bit = bits[idx]; - lbool val = ctx.get_assignment(bit); + lbool val = ctx.get_assignment(bit); if (val == l_undef) { continue; } @@ -1229,6 +1297,7 @@ namespace smt { SASSERT(bit != ~bit2); lbool val2 = ctx.get_assignment(bit2); TRACE("bv_bit_prop", tout << "propagating #" << get_enode(v2)->get_owner_id() << "[" << idx << "] = " << val2 << "\n";); + TRACE("bv", tout << bit << " " << bit2 << "\n";); if (val != val2) { literal consequent = bit2; @@ -1339,6 +1408,9 @@ namespace smt { void theory_bv::push_scope_eh() { theory::push_scope_eh(); m_trail_stack.push_scope(); +#if WATCH_DISEQ + m_diseq_watch_lim.push_back(m_diseq_watch_trail.size()); +#endif } void theory_bv::pop_scope_eh(unsigned num_scopes) { @@ -1348,6 +1420,16 @@ namespace smt { m_bits.shrink(num_old_vars); m_wpos.shrink(num_old_vars); m_zero_one_bits.shrink(num_old_vars); +#if WATCH_DISEQ + unsigned old_trail_sz = m_diseq_watch_lim[m_diseq_watch_lim.size()-num_scopes]; + for (unsigned i = m_diseq_watch_trail.size(); i-- > old_trail_sz;) { + if (!m_diseq_watch[m_diseq_watch_trail[i]].empty()) { + m_diseq_watch[m_diseq_watch_trail[i]].pop_back(); + } + } + m_diseq_watch_trail.shrink(old_trail_sz); + m_diseq_watch_lim.shrink(m_diseq_watch_lim.size()-num_scopes); +#endif theory::pop_scope_eh(num_scopes); } @@ -1390,6 +1472,8 @@ namespace smt { m_trail_stack(*this), m_find(*this), m_approximates_large_bvs(false) { + memset(m_eq_activity, 0, sizeof(m_eq_activity)); + memset(m_diseq_activity, 0, sizeof(m_diseq_activity)); } theory_bv::~theory_bv() { @@ -1512,6 +1596,17 @@ namespace smt { return true; } + void theory_bv::propagate() { + unsigned sz = m_replay_diseq.size(); + if (sz > 0) { + for (unsigned i = 0; i < sz; ++i) { + auto const& p = m_replay_diseq[i]; + expand_diseq(p.first, p.second); + } + m_replay_diseq.reset(); + } + } + class bit_eq_justification : public justification { enode * m_v1; enode * m_v2; diff --git a/src/smt/theory_bv.h b/src/smt/theory_bv.h index 23644c6fd..5a4edf4cf 100644 --- a/src/smt/theory_bv.h +++ b/src/smt/theory_bv.h @@ -124,6 +124,13 @@ namespace smt { value2var m_fixed_var_table; + unsigned char m_eq_activity[256]; + unsigned char m_diseq_activity[256]; + svector> m_replay_diseq; + vector>> m_diseq_watch; + svector m_diseq_watch_trail; + unsigned_vector m_diseq_watch_lim; + literal_vector m_tmp_literals; svector m_prop_queue; bool m_approximates_large_bvs; @@ -233,6 +240,8 @@ namespace smt { bool include_func_interp(func_decl* f) override; svector m_merge_aux[2]; //!< auxiliary vector used in merge_zero_one_bits bool merge_zero_one_bits(theory_var r1, theory_var r2); + bool can_propagate() override { return !m_replay_diseq.empty(); } + void propagate() override; // ----------------------------------- // diff --git a/src/smt/theory_datatype.cpp b/src/smt/theory_datatype.cpp index 9209d8b5d..0fac42501 100644 --- a/src/smt/theory_datatype.cpp +++ b/src/smt/theory_datatype.cpp @@ -481,33 +481,46 @@ namespace smt { return d->m_constructor; } + void theory_datatype::explain_is_child(enode* parent, enode* child) { + enode * parentc = oc_get_cstor(parent); + if (parent != parentc) { + m_used_eqs.push_back(enode_pair(parent, parentc)); + } + + // collect equalities on all children that may have been used. + bool found = false; + for (enode * arg : enode::args(parentc)) { + // found an argument which is equal to root + if (arg->get_root() == child->get_root()) { + if (arg != child) { + m_used_eqs.push_back(enode_pair(arg, child)); + } + found = true; + } + } + VERIFY(found); + } + // explain the cycle root -> ... -> app -> root void theory_datatype::occurs_check_explain(enode * app, enode * root) { TRACE("datatype", tout << "occurs_check_explain " << mk_bounded_pp(app->get_owner(), get_manager()) << " <-> " << mk_bounded_pp(root->get_owner(), get_manager()) << "\n";); - enode* app_parent = nullptr; // first: explain that root=v, given that app=cstor(...,v,...) - for (enode * arg : enode::args(oc_get_cstor(app))) { - // found an argument which is equal to root - if (arg->get_root() == root->get_root()) { - if (arg != root) - m_used_eqs.push_back(enode_pair(arg, root)); - break; - } - } + + explain_is_child(app, root); // now explain app=cstor(..,v,..) where v=root, and recurse with parent of app while (app->get_root() != root->get_root()) { - enode * app_cstor = oc_get_cstor(app); - if (app != app_cstor) - m_used_eqs.push_back(enode_pair(app, app_cstor)); - app_parent = m_parent[app->get_root()]; - app = app_parent; + enode* parent_app = m_parent[app->get_root()]; + explain_is_child(parent_app, app); + SASSERT(is_constructor(parent_app)); + app = parent_app; } SASSERT(app->get_root() == root->get_root()); - if (app != root) + if (app != root) { m_used_eqs.push_back(enode_pair(app, root)); + } TRACE("datatype", tout << "occurs_check\n"; @@ -732,11 +745,11 @@ 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)); + d->m_recognizers.resize(m_util.get_datatype_num_constructors(s), nullptr); } SASSERT(d->m_recognizers.size() == m_util.get_datatype_num_constructors(s)); unsigned c_idx = m_util.get_recognizer_constructor_idx(recognizer->get_decl()); - if (d->m_recognizers[c_idx] == 0) { + if (d->m_recognizers[c_idx] == nullptr) { lbool val = ctx.get_assignment(recognizer); TRACE("datatype", tout << "adding recognizer to v" << v << " rec: #" << recognizer->get_owner_id() << " val: " << val << "\n";); if (val == l_true) { @@ -808,7 +821,7 @@ namespace smt { region & reg = ctx.get_region(); TRACE("datatype_conflict", tout << mk_ismt2_pp(recognizer->get_owner(), get_manager()) << "\n"; for (literal l : lits) { - ctx.display_detailed_literal(tout, l); tout << "\n"; + ctx.display_detailed_literal(tout, l) << "\n"; } for (auto const& p : eqs) { tout << enode_eq_pp(p, ctx); diff --git a/src/smt/theory_datatype.h b/src/smt/theory_datatype.h index 010e78cb3..67cf5cfee 100644 --- a/src/smt/theory_datatype.h +++ b/src/smt/theory_datatype.h @@ -73,7 +73,7 @@ namespace smt { void sign_recognizer_conflict(enode * c, enode * r); typedef enum { ENTER, EXIT } stack_op; - typedef map, ptr_eq > parent_tbl; + typedef obj_map parent_tbl; typedef std::pair stack_entry; ptr_vector m_to_unmark; @@ -102,6 +102,7 @@ namespace smt { bool occurs_check(enode * n); bool occurs_check_enter(enode * n); void occurs_check_explain(enode * top, enode * root); + void explain_is_child(enode* parent, enode* child); void mk_split(theory_var v); diff --git a/src/smt/theory_jobscheduler.cpp b/src/smt/theory_jobscheduler.cpp index 802455683..e878594f3 100644 --- a/src/smt/theory_jobscheduler.cpp +++ b/src/smt/theory_jobscheduler.cpp @@ -305,7 +305,6 @@ namespace smt { literal end_ge_lo = mk_ge(ji.m_end, clb); // Initialization ensures that satisfiable states have completion time below end. - VERIFY(clb <= get_job_resource(j, r).m_end); ast_manager& m = get_manager(); if (m.has_trace_stream()) log_axiom_instantiation(m.mk_implies(m.mk_and(m.mk_eq(eq.first->get_owner(), eq.second->get_owner()), ctx.bool_var2expr(start_ge_lo.var())), ctx.bool_var2expr(end_ge_lo.var()))); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; @@ -324,6 +323,10 @@ namespace smt { */ bool theory_jobscheduler::constrain_end_time_interval(unsigned j, unsigned r) { unsigned idx1 = 0, idx2 = 0; + if (!job_has_resource(j, r)) { + IF_VERBOSE(0, verbose_stream() << "job " << j << " assigned non-registered resource " << r << "\n"); + return false; + } time_t s = start(j); job_resource const& jr = get_job_resource(j, r); TRACE("csp", tout << "job: " << j << " resource: " << r << " start: " << s << "\n";); @@ -457,6 +460,7 @@ namespace smt { job_info const& ji = m_jobs[j]; VERIFY(u.is_resource(ji.m_job2resource->get_root()->get_owner(), r)); TRACE("csp", tout << "job: " << j << " resource: " << r << "\n";); + std::cout << j << " -o " << r << "\n"; propagate_job2resource(j, r); } } @@ -464,8 +468,13 @@ namespace smt { void theory_jobscheduler::propagate_job2resource(unsigned j, unsigned r) { job_info const& ji = m_jobs[j]; res_info const& ri = m_resources[r]; - job_resource const& jr = get_job_resource(j, r); literal eq = mk_eq_lit(ji.m_job2resource, ri.m_resource); + if (!job_has_resource(j, r)) { + IF_VERBOSE(0, verbose_stream() << "job " << j << " assigned non-registered resource " << r << "\n"); + return; + } + return; + job_resource const& jr = get_job_resource(j, r); assert_last_end_time(j, r, jr, eq); assert_last_start_time(j, r, eq); assert_first_start_time(j, r, eq); @@ -495,7 +504,7 @@ namespace smt { } std::ostream& theory_jobscheduler::display(std::ostream & out, job_resource const& jr) const { - return out << "r:" << jr.m_resource_id << " cap:" << jr.m_capacity << " load:" << jr.m_loadpct << " end:" << jr.m_end; + return out << "r:" << jr.m_resource_id << " cap:" << jr.m_capacity << " load:" << jr.m_loadpct << " end:" << jr.m_finite_capacity_end; for (auto const& s : jr.m_properties) out << " " << s; out << "\n"; } @@ -557,7 +566,8 @@ namespace smt { } time_t theory_jobscheduler::get_lo(expr* e) { - arith_value av(get_context()); + arith_value av(m); + av.init(&get_context()); rational val; bool is_strict; if (av.get_lo(e, val, is_strict) && !is_strict && val.is_uint64()) { @@ -567,7 +577,8 @@ namespace smt { } time_t theory_jobscheduler::get_up(expr* e) { - arith_value av(get_context()); + arith_value av(m); + av.init(&get_context()); rational val; bool is_strict; if (av.get_up(e, val, is_strict) && !is_strict && val.is_uint64()) { @@ -577,9 +588,10 @@ namespace smt { } time_t theory_jobscheduler::get_value(expr* e) { - arith_value av(get_context()); + arith_value av(get_manager()); + av.init(&get_context()); rational val; - if (av.get_value(e, val) && val.is_uint64()) { + if (av.get_value_equiv(e, val) && val.is_uint64()) { return val.get_uint64(); } return 0; @@ -623,21 +635,30 @@ namespace smt { } void theory_jobscheduler::set_preemptable(unsigned j, bool is_preemptable) { - m_jobs.reserve(j + 1); - m_jobs[j].m_is_preemptable = is_preemptable; + ensure_job(j).m_is_preemptable = is_preemptable; } - void theory_jobscheduler::add_job_resource(unsigned j, unsigned r, unsigned loadpct, uint64_t cap, time_t end, properties const& ps) { - SASSERT(get_context().at_base_level()); - SASSERT(0 <= loadpct && loadpct <= 100); - SASSERT(0 <= cap); - m_jobs.reserve(j + 1); - m_resources.reserve(r + 1); - job_info& ji = m_jobs[j]; - if (ji.m_resource2index.contains(r)) { - throw default_exception("resource already bound to job"); + theory_jobscheduler::res_info& theory_jobscheduler::ensure_resource(unsigned last) { + while (m_resources.size() <= last) { + unsigned r = m_resources.size(); + m_resources.push_back(res_info()); + res_info& ri = m_resources.back(); + context& ctx = get_context(); + app_ref res(u.mk_resource(r), m); + if (!ctx.e_internalized(res)) ctx.internalize(res, false); + ri.m_resource = ctx.get_enode(res); + app_ref ms(u.mk_makespan(r), m); + if (!ctx.e_internalized(ms)) ctx.internalize(ms, false); + ri.m_makespan = ctx.get_enode(ms); } - if (!ji.m_start) { + return m_resources[last]; + } + + theory_jobscheduler::job_info& theory_jobscheduler::ensure_job(unsigned last) { + while (m_jobs.size() <= last) { + unsigned j = m_jobs.size(); + m_jobs.push_back(job_info()); + job_info& ji = m_jobs.back(); context& ctx = get_context(); app_ref job(u.mk_job(j), m); app_ref start(u.mk_start(j), m); @@ -652,10 +673,22 @@ namespace smt { ji.m_end = ctx.get_enode(end); ji.m_job2resource = ctx.get_enode(res); } + return m_jobs[last]; + } + + void theory_jobscheduler::add_job_resource(unsigned j, unsigned r, unsigned loadpct, uint64_t cap, time_t finite_capacity_end, properties const& ps) { + SASSERT(get_context().at_base_level()); + SASSERT(0 <= loadpct && loadpct <= 100); + SASSERT(0 <= cap); + job_info& ji = ensure_job(j); + res_info& ri = ensure_resource(r); + if (ji.m_resource2index.contains(r)) { + throw default_exception("resource already bound to job"); + } ji.m_resource2index.insert(r, ji.m_resources.size()); - ji.m_resources.push_back(job_resource(r, cap, loadpct, end, ps)); - SASSERT(!m_resources[r].m_jobs.contains(j)); - m_resources[r].m_jobs.push_back(j); + ji.m_resources.push_back(job_resource(r, cap, loadpct, finite_capacity_end, ps)); + SASSERT(!ri.m_jobs.contains(j)); + ri.m_jobs.push_back(j); } @@ -663,17 +696,7 @@ namespace smt { SASSERT(get_context().at_base_level()); SASSERT(1 <= max_loadpct && max_loadpct <= 100); SASSERT(start <= end); - m_resources.reserve(r + 1); - res_info& ri = m_resources[r]; - if (!ri.m_resource) { - context& ctx = get_context(); - app_ref res(u.mk_resource(r), m); - if (!ctx.e_internalized(res)) ctx.internalize(res, false); - ri.m_resource = ctx.get_enode(res); - app_ref ms(u.mk_makespan(r), m); - if (!ctx.e_internalized(ms)) ctx.internalize(ms, false); - ri.m_makespan = ctx.get_enode(ms); - } + res_info& ri = ensure_resource(r); ri.m_available.push_back(res_available(max_loadpct, start, end, ps)); } @@ -723,22 +746,37 @@ namespace smt { for (job_resource const& jr : ji.m_resources) { unsigned r = jr.m_resource_id; res_info const& ri = m_resources[r]; - if (ri.m_available.empty()) continue; + if (ri.m_available.empty()) { + if (jr.m_capacity == 0) { + start_lb = 0; + end_ub = std::numeric_limits::max(); + runtime_lb = 0; + } + continue; + } unsigned idx = 0; if (first_available(jr, ri, idx)) { start_lb = std::min(start_lb, ri.m_available[idx].m_start); } + else { + IF_VERBOSE(0, verbose_stream() << "not first-available\n";); + } idx = ri.m_available.size(); if (last_available(jr, ri, idx)) { end_ub = std::max(end_ub, ri.m_available[idx].m_end); } + else { + IF_VERBOSE(0, verbose_stream() << "not last-available\n";); + } runtime_lb = std::min(runtime_lb, jr.m_capacity); // TBD: more accurate estimates for runtime_lb based on gaps // TBD: correct estimate of runtime_ub taking gaps into account. } CTRACE("csp", (start_lb > end_ub), tout << "there is no associated resource working time\n";); if (start_lb > end_ub) { + IF_VERBOSE(0, verbose_stream() << start_lb << " " << end_ub << "\n"); warning_msg("Job %d has no associated resource working time", job_id); + continue; } // start(j) >= start_lb @@ -764,12 +802,14 @@ namespace smt { // resource(j) = r => end(j) <= end(j, r) void theory_jobscheduler::assert_last_end_time(unsigned j, unsigned r, job_resource const& jr, literal eq) { +#if 0 job_info const& ji = m_jobs[j]; - literal l2 = mk_le(ji.m_end, jr.m_end); + literal l2 = mk_le(ji.m_end, jr.m_finite_capacity_end); context& ctx = get_context(); if (m.has_trace_stream()) log_axiom_instantiation(get_manager().mk_implies(ctx.bool_var2expr(eq.var()), ctx.bool_var2expr(l2.var()))); ctx.mk_th_axiom(get_id(), ~eq, l2); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; +#endif } // resource(j) = r => start(j) <= lst(j, r, end(j, r)) @@ -916,6 +956,10 @@ namespace smt { } } + bool theory_jobscheduler::job_has_resource(unsigned j, unsigned r) const { + return m_jobs[j].m_resource2index.contains(r); + } + theory_jobscheduler::job_resource const& theory_jobscheduler::get_job_resource(unsigned j, unsigned r) const { job_info const& ji = m_jobs[j]; return ji.m_resources[ji.m_resource2index[r]]; @@ -1067,7 +1111,9 @@ namespace smt { bool theory_jobscheduler::resource_available(job_resource const& jr, res_available const& ra) const { auto const& jps = jr.m_properties; auto const& rps = ra.m_properties; - if (jps.size() > rps.size()) return false; + if (jps.size() > rps.size()) { + return false; + } unsigned j = 0, i = 0; for (; i < jps.size() && j < rps.size(); ) { if (jps[i] == rps[j]) { diff --git a/src/smt/theory_jobscheduler.h b/src/smt/theory_jobscheduler.h index 3b9a42595..55ece4da4 100644 --- a/src/smt/theory_jobscheduler.h +++ b/src/smt/theory_jobscheduler.h @@ -38,10 +38,10 @@ namespace smt { unsigned m_resource_id; // id of resource time_t m_capacity; // amount of resource to use unsigned m_loadpct; // assuming loadpct - time_t m_end; // must run before + time_t m_finite_capacity_end; properties m_properties; job_resource(unsigned r, time_t cap, unsigned loadpct, time_t end, properties const& ps): - m_resource_id(r), m_capacity(cap), m_loadpct(loadpct), m_end(end), m_properties(ps) {} + m_resource_id(r), m_capacity(cap), m_loadpct(loadpct), m_finite_capacity_end(end), m_properties(ps) {} }; struct job_time { @@ -89,10 +89,9 @@ namespace smt { struct res_info { unsigned_vector m_jobs; // jobs allocated to run on resource vector m_available; // time intervals where resource is available - time_t m_end; // can't run after enode* m_resource; enode* m_makespan; - res_info(): m_end(std::numeric_limits::max()), m_resource(nullptr), m_makespan(nullptr) {} + res_info(): m_resource(nullptr), m_makespan(nullptr) {} }; ast_manager& m; @@ -152,6 +151,9 @@ namespace smt { theory * mk_fresh(context * new_ctx) override; + res_info& ensure_resource(unsigned r); + job_info& ensure_job(unsigned j); + public: // set up job/resource global constraints void set_preemptable(unsigned j, bool is_preemptable); @@ -189,6 +191,7 @@ namespace smt { time_t capacity_used(unsigned j, unsigned r, time_t start, time_t end); // capacity used between start and end job_resource const& get_job_resource(unsigned j, unsigned r) const; + bool job_has_resource(unsigned j, unsigned r) const; // propagation void propagate_end_time(unsigned j, unsigned r); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 2fad2f97b..6961f8e0b 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -323,6 +323,10 @@ class theory_lra::imp { m_solver->settings().m_int_run_gcd_test = ctx().get_fparams().m_arith_gcd_test; m_solver->settings().set_random_seed(ctx().get_fparams().m_random_seed); m_lia = alloc(lp::int_solver, m_solver.get()); + get_one(true); + get_zero(true); + get_one(false); + get_zero(false); } void ensure_nra() { @@ -479,22 +483,6 @@ class theory_lra::imp { if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod); } else if (a.is_mod(n, n1, n2)) { - bool is_num = a.is_numeral(n2, r) && !r.is_zero(); - if (!is_num) { - found_not_handled(n); - } -#if 0 - else { - app_ref div(a.mk_idiv(n1, n2), m); - mk_enode(div); - theory_var w = mk_var(div); - theory_var u = mk_var(n1); - // add axioms: - // u = v + r*w - // abs(r) > v >= 0 - assert_idiv_mod_axioms(u, v, w, r); - } -#endif if (!ctx().relevancy()) mk_idiv_mod_axioms(n1, n2); } else if (a.is_rem(n, n1, n2)) { @@ -2341,16 +2329,14 @@ public: iterator lo_inf = begin1, lo_sup = begin1; iterator hi_inf = begin2, hi_sup = begin2; - iterator lo_inf1 = begin1, lo_sup1 = begin1; - iterator hi_inf1 = begin2, hi_sup1 = begin2; bool flo_inf, fhi_inf, flo_sup, fhi_sup; ptr_addr_hashtable visited; for (unsigned i = 0; i < atoms.size(); ++i) { lp_api::bound* a1 = atoms[i]; - lo_inf1 = next_inf(a1, lp_api::lower_t, lo_inf, end, flo_inf); - hi_inf1 = next_inf(a1, lp_api::upper_t, hi_inf, end, fhi_inf); - lo_sup1 = next_sup(a1, lp_api::lower_t, lo_sup, end, flo_sup); - hi_sup1 = next_sup(a1, lp_api::upper_t, hi_sup, end, fhi_sup); + iterator lo_inf1 = next_inf(a1, lp_api::lower_t, lo_inf, end, flo_inf); + iterator hi_inf1 = next_inf(a1, lp_api::upper_t, hi_inf, end, fhi_inf); + iterator lo_sup1 = next_sup(a1, lp_api::lower_t, lo_sup, end, flo_sup); + iterator hi_sup1 = next_sup(a1, lp_api::upper_t, hi_sup, end, fhi_sup); if (lo_inf1 != end) lo_inf = lo_inf1; if (lo_sup1 != end) lo_sup = lo_sup1; if (hi_inf1 != end) hi_inf = hi_inf1; @@ -2730,14 +2716,12 @@ public: if (propagate_eqs()) { rational const& value = b.get_value(); if (k == lp::GE) { - set_lower_bound(vi, ci, value); - if (has_upper_bound(vi, ci, value)) { + if (set_lower_bound(vi, ci, value) && has_upper_bound(vi, ci, value)) { fixed_var_eh(b.get_var(), value); } } else if (k == lp::LE) { - set_upper_bound(vi, ci, value); - if (has_lower_bound(vi, ci, value)) { + if (set_upper_bound(vi, ci, value) && has_lower_bound(vi, ci, value)) { fixed_var_eh(b.get_var(), value); } } @@ -2756,25 +2740,39 @@ public: bool use_tableau() const { return lp_params(ctx().get_params()).simplex_strategy() < 2; } - void set_upper_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { set_bound(vi, ci, v, false); } + bool set_upper_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { return set_bound(vi, ci, v, false); } - void set_lower_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { set_bound(vi, ci, v, true); } + bool set_lower_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { return set_bound(vi, ci, v, true); } - void set_bound(lp::var_index vi, lp::constraint_index ci, rational const& v, bool is_lower) { - if (!m_solver->is_term(vi)) { + bool set_bound(lp::var_index vi, lp::constraint_index ci, rational const& v, bool is_lower) { + + if (m_solver->is_term(vi)) { + lp::var_index ti = m_solver->adjust_term_index(vi); + auto& vec = is_lower ? m_lower_terms : m_upper_terms; + if (vec.size() <= ti) { + vec.resize(ti + 1, constraint_bound(UINT_MAX, rational())); + } + constraint_bound& b = vec[ti]; + if (b.first == UINT_MAX || (is_lower? b.second < v : b.second > v)) { + TRACE("arith", tout << "tighter bound " << vi << "\n";); + ctx().push_trail(vector_value_trail(vec, ti)); + b.first = ci; + b.second = v; + } + return true; + } + else { + TRACE("arith", tout << "not a term " << vi << "\n";); // m_solver already tracks bounds on proper variables, but not on terms. - return; - } - lp::var_index ti = m_solver->adjust_term_index(vi); - auto& vec = is_lower ? m_lower_terms : m_upper_terms; - if (vec.size() <= ti) { - vec.resize(ti + 1, constraint_bound(UINT_MAX, rational())); - } - constraint_bound& b = vec[ti]; - if (b.first == UINT_MAX || (is_lower? b.second < v : b.second > v)) { - ctx().push_trail(vector_value_trail(vec, ti)); - b.first = ci; - b.second = v; + bool is_strict = false; + rational b; + if (is_lower) { + return m_solver->has_lower_bound(vi, ci, b, is_strict) && !is_strict && b == v; + } + else { + return m_solver->has_upper_bound(vi, ci, b, is_strict) && !is_strict && b == v; + } + } } diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index 90d1a5481..875fe8de4 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -158,8 +158,6 @@ namespace smt { m_watch_sz = 0; m_watch_sum.reset(); m_num_propagations = 0; - m_compilation_threshold = UINT_MAX; - m_compiled = l_false; m_args[0].reset(); m_args[0].m_k.reset(); m_args[1].reset(); @@ -430,10 +428,6 @@ namespace smt { void theory_pb::card::inc_propagations(theory_pb& th) { ++m_num_propagations; - if (m_compiled == l_false && m_num_propagations >= m_compilation_threshold) { - // m_compiled = l_undef; - // th.m_to_compile.push_back(&c); - } } // ------------------------ @@ -443,8 +437,6 @@ namespace smt { theory(m.mk_family_id("pb")), m_params(p), pb(m), - m_max_compiled_coeff(rational(8)), - m_cardinality_lemma(false), m_restart_lim(3), m_restart_inc(0), m_antecedent_exprs(m), @@ -452,7 +444,6 @@ namespace smt { { m_learn_complements = p.m_pb_learn_complements; m_conflict_frequency = p.m_pb_conflict_frequency; - m_enable_compilation = p.m_pb_enable_compilation; } theory_pb::~theory_pb() { @@ -465,7 +456,8 @@ namespace smt { bool theory_pb::internalize_atom(app * atom, bool gate_ctx) { context& ctx = get_context(); - TRACE("pb", tout << mk_pp(atom, get_manager()) << "\n";); + ast_manager& m = get_manager(); + if (ctx.b_internalized(atom)) { return true; } @@ -490,6 +482,38 @@ namespace smt { unsigned num_args = atom->get_num_args(); bool_var abv = ctx.mk_bool_var(atom); ctx.set_var_theory(abv, get_id()); + literal lit(abv); + + if (pb.is_eq(atom)) { + expr_ref_vector args(m), nargs(m); + vector coeffs; + rational sum(0); + for (unsigned i = 0; i < num_args; ++i) { + args.push_back(atom->get_arg(i)); + nargs.push_back(::mk_not(m, atom->get_arg(i))); + rational c = pb.get_coeff(atom, i); + coeffs.push_back(c); + sum += c; + } + rational k = pb.get_k(atom); + // ax + by + cz <= k + // <=> + // -ax - by - cz >= -k + // <=> + // a(1-x) + b(1-y) + c(1-z) >= a + b + c - k + expr_ref le(pb.mk_ge(num_args, coeffs.c_ptr(), nargs.c_ptr(), sum - k), m); + expr_ref ge(pb.mk_ge(num_args, coeffs.c_ptr(), args.c_ptr(), k), m); + ctx.internalize(le, false); + ctx.internalize(ge, false); + literal le_lit = ctx.get_literal(le); + literal ge_lit = ctx.get_literal(ge); + ctx.mark_as_relevant(le_lit); + ctx.mark_as_relevant(ge_lit); + ctx.mk_th_axiom(get_id(), ~lit, le_lit); + ctx.mk_th_axiom(get_id(), ~lit, ge_lit); + ctx.mk_th_axiom(get_id(), ~le_lit, ~ge_lit, lit); + return true; + } ineq* c = alloc(ineq, m_mpz_mgr, literal(abv), pb.is_eq(atom)); c->m_args[0].m_k = pb.get_k(atom); @@ -512,34 +536,23 @@ namespace smt { break; } } + if (pb.is_at_most_k(atom) || pb.is_le(atom)) { - // turn W <= k into -W >= -k - for (unsigned i = 0; i < args.size(); ++i) { - args[i].second = -args[i].second; - } k = -k; + for (auto& a : args) { + a.first.neg(); + k += a.second; + } } - else { - SASSERT(pb.is_at_least_k(atom) || pb.is_ge(atom) || pb.is_eq(atom)); - } - TRACE("pb", display(tout, *c, true);); - //app_ref fml1(m), fml2(m); - //fml1 = c->to_expr(ctx, m); c->unique(); lbool is_true = c->normalize(); c->prune(); c->post_prune(); - //fml2 = c->to_expr(ctx, m); - //expr_ref validate_pb = pb_rewriter(m).mk_validate_rewrite(fml1, fml2); - //pb_rewriter(m).dump_pb_rewrite(validate_pb); - literal lit(abv); - - - TRACE("pb", display(tout, *c); tout << " := " << lit << "\n";); - switch(is_true) { + TRACE("pb", display(tout, *c); tout << " := " << lit << " " << is_true << "\n";); + switch (is_true) { case l_false: - lit = ~lit; + lit.neg(); // fall-through case l_true: ctx.mk_th_axiom(get_id(), 1, &lit); @@ -564,40 +577,18 @@ namespace smt { // maximal coefficient: scoped_mpz& max_watch = c->m_max_watch; max_watch.reset(); - for (unsigned i = 0; i < args.size(); ++i) { - mpz const& num = args[i].second.to_mpq().numerator(); + for (auto const& a : args) { + mpz const& num = a.second.to_mpq().numerator(); if (m_mpz_mgr.lt(max_watch, num)) { max_watch = num; } } - // pre-compile threshold for cardinality - bool enable_compile = m_enable_compilation && c->is_ge() && !c->k().is_one(); - for (unsigned i = 0; enable_compile && i < args.size(); ++i) { - enable_compile = (args[i].second <= m_max_compiled_coeff); - } - if (enable_compile) { - unsigned log = 1, n = 1; - while (n <= args.size()) { - ++log; - n *= 2; - } - unsigned th = args.size()*log*log; - c->m_compilation_threshold = th; - IF_VERBOSE(2, verbose_stream() << "(smt.pb setting compilation threshold to " << th << ")\n";); - TRACE("pb", tout << "compilation threshold: " << th << "\n";); - } - else { - c->m_compilation_threshold = UINT_MAX; - } - init_watch_var(*c); + init_watch_ineq(*c); init_watch(abv); m_var_infos[abv].m_ineq = c; m_ineqs_trail.push_back(abv); - - TRACE("pb", display(tout, *c);); - return true; } @@ -699,7 +690,6 @@ namespace smt { } } - void theory_pb::watch_literal(literal lit, ineq* c) { init_watch(lit.var()); ptr_vector* ineqs = m_var_infos[lit.var()].m_lit_watch[lit.sign()]; @@ -707,27 +697,14 @@ namespace smt { ineqs = alloc(ptr_vector); m_var_infos[lit.var()].m_lit_watch[lit.sign()] = ineqs; } + DEBUG_CODE( + for (auto* c1 : *ineqs) { + //if (c1 == c) return; + SASSERT (c1 != c); + }); ineqs->push_back(c); } - - void theory_pb::watch_var(bool_var v, ineq* c) { - init_watch(v); - ptr_vector* ineqs = m_var_infos[v].m_var_watch; - if (ineqs == nullptr) { - ineqs = alloc(ptr_vector); - m_var_infos[v].m_var_watch = ineqs; - } - ineqs->push_back(c); - } - - void theory_pb::unwatch_var(bool_var v, ineq* c) { - ptr_vector* ineqs = m_var_infos[v].m_var_watch; - if (ineqs) { - remove(*ineqs, c); - } - } - void theory_pb::unwatch_literal(literal lit, ineq* c) { ptr_vector* ineqs = m_var_infos[lit.var()].m_lit_watch[lit.sign()]; if (ineqs) { @@ -736,13 +713,14 @@ namespace smt { } void theory_pb::remove(ptr_vector& ineqs, ineq* c) { - for (unsigned j = 0; j < ineqs.size(); ++j) { + unsigned sz = ineqs.size(); + for (unsigned j = 0; j < sz; ++j) { if (ineqs[j] == c) { - std::swap(ineqs[j], ineqs[ineqs.size()-1]); + std::swap(ineqs[j], ineqs[sz-1]); ineqs.pop_back(); - break; + return; } - } + } } // ---------------------------- @@ -792,8 +770,6 @@ namespace smt { return m.mk_th_lemma(m_fid, fact, prs.size(), prs.c_ptr()); } } - - }; @@ -964,9 +940,9 @@ namespace smt { justification* js = nullptr; c.inc_propagations(*this); if (!resolve_conflict(c, lits)) { - if (proofs_enabled()) { - js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); - } + if (proofs_enabled()) { + js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); + } ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, nullptr); } SASSERT(ctx.inconsistent()); @@ -997,9 +973,6 @@ namespace smt { st.update("pb conflicts", m_stats.m_num_conflicts); st.update("pb propagations", m_stats.m_num_propagations); st.update("pb predicates", m_stats.m_num_predicates); - st.update("pb compilations", m_stats.m_num_compiles); - st.update("pb compiled clauses", m_stats.m_num_compiled_clauses); - st.update("pb compiled vars", m_stats.m_num_compiled_vars); } void theory_pb::reset_eh() { @@ -1012,8 +985,6 @@ namespace smt { m_card_trail.reset(); m_card_lim.reset(); m_stats.reset(); - m_to_compile.reset(); - m_cardinality_lemma = false; } void theory_pb::new_eq_eh(theory_var v1, theory_var v2) { @@ -1042,21 +1013,10 @@ namespace smt { } } } - ineqs = m_var_infos[v].m_var_watch; - if (ineqs != nullptr) { - for (unsigned i = 0; i < ineqs->size(); ++i) { - ineq* c = (*ineqs)[i]; - assign_watch(v, is_true, *c); - } - } ineq* c = m_var_infos[v].m_ineq; if (c != nullptr) { - if (c->is_ge()) { - assign_ineq(*c, is_true); - } - else { - assign_eq(*c, is_true); - } + VERIFY(c->is_ge()); + assign_ineq(*c, is_true); } ptr_vector* cards = m_var_infos[v].m_lit_cwatch[nlit.sign()]; @@ -1143,19 +1103,6 @@ namespace smt { return lits; } - class theory_pb::rewatch_vars : public trail { - theory_pb& pb; - ineq& c; - public: - rewatch_vars(theory_pb& p, ineq& c): pb(p), c(c) {} - void undo(context& ctx) override { - for (unsigned i = 0; i < c.size(); ++i) { - pb.watch_var(c.lit(i).var(), &c); - } - } - }; - - class theory_pb::negate_ineq : public trail { ineq& c; public: @@ -1176,7 +1123,6 @@ namespace smt { ctx.push_trail(value_trail(c.m_max_sum)); ctx.push_trail(value_trail(c.m_min_sum)); ctx.push_trail(value_trail(c.m_nfixed)); - ctx.push_trail(rewatch_vars(*this, c)); SASSERT(c.is_ge()); unsigned sz = c.size(); @@ -1214,10 +1160,15 @@ namespace smt { // perform unit propagation if (maxsum >= c.mpz_k() && maxsum - mininc < c.mpz_k()) { literal_vector& lits = get_unhelpful_literals(c, true); + // for (literal lit : lits) SASSERT(ctx.get_assignment(lit) == l_true); lits.push_back(c.lit()); + // SASSERT(ctx.get_assignment(c.lit()) == l_true); for (unsigned i = 0; i < sz; ++i) { - DEBUG_CODE(validate_assign(c, lits, c.lit(i));); - add_assign(c, lits, c.lit(i)); + literal lit = c.lit(i); + if (ctx.get_assignment(lit) == l_undef) { + DEBUG_CODE(validate_assign(c, lits, lit);); + add_assign(c, lits, c.lit(i)); + } } } } @@ -1227,104 +1178,7 @@ namespace smt { */ void theory_pb::assign_eq(ineq& c, bool is_true) { SASSERT(c.is_eq()); - - } - - /** - Propagation rules: - - nfixed = N & minsum = k -> T - nfixed = N & minsum != k -> F - - minsum > k or maxsum < k -> F - minsum = k & = -> fix 0 variables - nfixed+1 = N & = -> fix unassigned variable or conflict - nfixed+1 = N & != -> maybe forced unassigned to ensure disequal - minsum >= k -> T - maxsum < k -> F - */ - - void theory_pb::assign_watch(bool_var v, bool is_true, ineq& c) { - - context& ctx = get_context(); - unsigned i; - literal l = c.lit(); - lbool asgn = ctx.get_assignment(l); - - if (c.max_sum() < c.mpz_k() && asgn == l_false) { - return; - } - if (c.is_ge() && c.min_sum() >= c.mpz_k() && asgn == l_true) { - return; - } - for (i = 0; i < c.size(); ++i) { - if (c.lit(i).var() == v) { - break; - } - } - - TRACE("pb", display(tout << "assign watch " << literal(v,!is_true) << " ", c, true);); - - SASSERT(i < c.size()); - if (c.lit(i).sign() == is_true) { - ctx.push_trail(value_trail(c.m_max_sum)); - c.m_max_sum -= c.ncoeff(i); - } - else { - ctx.push_trail(value_trail(c.m_min_sum)); - c.m_min_sum += c.ncoeff(i); - } - DEBUG_CODE( - scoped_mpz sum(m_mpz_mgr); - scoped_mpz maxs(m_mpz_mgr); - for (unsigned i = 0; i < c.size(); ++i) { - if (ctx.get_assignment(c.lit(i)) == l_true) sum += c.ncoeff(i); - if (ctx.get_assignment(c.lit(i)) != l_false) maxs += c.ncoeff(i); - } - CTRACE("pb", (maxs > c.max_sum()), display(tout, c, true);); - SASSERT(c.min_sum() <= sum); - SASSERT(sum <= maxs); - SASSERT(maxs <= c.max_sum()); - ); - SASSERT(c.min_sum() <= c.max_sum()); - SASSERT(!m_mpz_mgr.is_neg(c.min_sum())); - ctx.push_trail(value_trail(c.m_nfixed)); - ++c.m_nfixed; - SASSERT(c.nfixed() <= c.size()); - if (c.is_ge() && c.min_sum() >= c.mpz_k() && asgn != l_true) { - TRACE("pb", display(tout << "Set " << l << "\n", c, true);); - add_assign(c, get_helpful_literals(c, false), l); - } - else if (c.max_sum() < c.mpz_k() && asgn != l_false) { - TRACE("pb", display(tout << "Set " << ~l << "\n", c, true);); - add_assign(c, get_unhelpful_literals(c, true), ~l); - } - else if (c.is_eq() && c.nfixed() == c.size() && c.min_sum() == c.mpz_k() && asgn != l_true) { - TRACE("pb", display(tout << "Set " << l << "\n", c, true);); - add_assign(c, get_all_literals(c, false), l); - } - else if (c.is_eq() && c.nfixed() == c.size() && c.min_sum() != c.mpz_k() && asgn != l_false) { - TRACE("pb", display(tout << "Set " << ~l << "\n", c, true);); - add_assign(c, get_all_literals(c, false), ~l); - } -#if 0 - else if (c.is_eq() && c.min_sum() > c.mpz_k() && asgn != l_false) { - TRACE("pb", display(tout << "Set " << ~l << "\n", c, true);); - add_assign(c, get_all_literals(c, false), ~l); - } - else if (c.is_eq() && asgn == l_true && c.min_sum() == c.mpz_k() && c.max_sum() > c.mpz_k()) { - literal_vector& lits = get_all_literals(c, false); - lits.push_back(c.lit()); - for (unsigned i = 0; i < c.size(); ++i) { - if (ctx.get_assignment(c.lit(i)) == l_undef) { - add_assign(c, lits, ~c.lit(i)); - } - } - } -#endif - else { - IF_VERBOSE(14, display(verbose_stream() << "no propagation ", c, true);); - } + UNREACHABLE(); } @@ -1479,31 +1333,9 @@ namespace smt { void theory_pb::inc_propagations(ineq& c) { ++c.m_num_propagations; - if (c.m_compiled == l_false && c.m_num_propagations >= c.m_compilation_threshold) { - c.m_compiled = l_undef; - m_to_compile.push_back(&c); - } } void theory_pb::restart_eh() { - for (unsigned i = 0; i < m_to_compile.size(); ++i) { - compile_ineq(*m_to_compile[i]); - } - m_to_compile.reset(); - - return; - - if (m_restart_lim <= m_restart_inc) { - m_restart_inc = 0; - if (gc()) { - m_restart_lim = 3; - } - else { - m_restart_lim *= 4; - m_restart_lim /= 3; - } - } - ++m_restart_inc; } bool theory_pb::gc() { @@ -1553,7 +1385,7 @@ namespace smt { } } - std::cout << "zs: " << z << " nzs: " << nz << " lemmas: " << ctx.get_lemmas().size() << " trail: " << m_card_trail.size() << "\n"; + //std::cout << "zs: " << z << " nzs: " << nz << " lemmas: " << ctx.get_lemmas().size() << " trail: " << m_card_trail.size() << "\n"; return z*10 >= nz; m_occs.reset(); @@ -1568,76 +1400,6 @@ namespace smt { } - void theory_pb::compile_ineq(ineq& c) { - ++m_stats.m_num_compiles; - context& ctx = get_context(); - // only cardinality constraints are compiled. - SASSERT(c.m_compilation_threshold < UINT_MAX); - DEBUG_CODE(for (unsigned i = 0; i < c.size(); ++i) SASSERT(c.coeff(i).is_int()); ); - unsigned k = c.k().get_unsigned(); - unsigned num_args = c.size(); - - - literal thl = c.lit(); - literal at_least_k; - - literal_vector in; - for (unsigned i = 0; i < num_args; ++i) { - rational n = c.coeff(i); - literal lit = c.lit(i); - lbool val = ctx.get_assignment(lit); - if (val != l_undef && ctx.get_assign_level(lit) == ctx.get_base_level()) { - if (val == l_true) { - unsigned m = n.get_unsigned(); - if (k < m) { - return; - } - k -= m; - } - continue; - } - while (n.is_pos()) { - in.push_back(c.lit(i)); - n -= rational::one(); - } - } - - TRACE("pb", tout << in << " >= " << k << "\n";); - - - psort_expr ps(ctx, *this); - psort_nw sortnw(ps); - sortnw.m_stats.reset(); - - if (ctx.get_assignment(thl) == l_true && - ctx.get_assign_level(thl) == ctx.get_base_level()) { - at_least_k = sortnw.ge(false, k, in.size(), in.c_ptr()); - TRACE("pb", tout << ~thl << " " << at_least_k << "\n";); - ctx.mk_clause(~thl, at_least_k, justify(~thl, at_least_k)); - } - else { - literal at_least_k = sortnw.ge(true, k, in.size(), in.c_ptr()); - TRACE("pb", tout << ~thl << " " << at_least_k << "\n";); - ctx.mk_clause(~thl, at_least_k, justify(~thl, at_least_k)); - ctx.mk_clause(~at_least_k, thl, justify(thl, ~at_least_k)); - } - m_stats.m_num_compiled_vars += sortnw.m_stats.m_num_compiled_vars; - m_stats.m_num_compiled_clauses += sortnw.m_stats.m_num_compiled_clauses; - - IF_VERBOSE(2, verbose_stream() - << "(smt.pb compile sorting network bound: " - << k << " literals: " << in.size() - << " clauses: " << sortnw.m_stats.m_num_compiled_clauses - << " vars: " << sortnw.m_stats.m_num_compiled_vars << ")\n";); - - // auxiliary clauses get removed when popping scopes. - // we have to recompile the circuit after back-tracking. - c.m_compiled = l_false; - ctx.push_trail(value_trail(c.m_compiled)); - c.m_compiled = l_true; - } - - void theory_pb::init_search_eh() { } @@ -1657,7 +1419,6 @@ namespace smt { clear_watch(*c); m_var_infos[v].m_ineq = nullptr; m_ineqs_trail.pop_back(); - m_to_compile.erase(c); dealloc(c); } m_ineqs_lim.resize(new_lim); @@ -1682,7 +1443,6 @@ namespace smt { void theory_pb::clear_watch(ineq& c) { for (unsigned i = 0; i < c.size(); ++i) { literal w = c.lit(i); - unwatch_var(w.var(), &c); unwatch_literal(w, &c); } c.m_watch_sum.reset(); @@ -1728,7 +1488,7 @@ namespace smt { ctx.push_trail(unwatch_ge(*this, c)); } - void theory_pb::init_watch_var(ineq& c) { + void theory_pb::init_watch_ineq(ineq& c) { c.m_min_sum.reset(); c.m_max_sum.reset(); c.m_nfixed = 0; @@ -1736,7 +1496,6 @@ namespace smt { c.m_max_watch.reset(); c.m_watch_sz = 0; for (unsigned i = 0; i < c.size(); ++i) { - watch_var(c.lit(i).var(), &c); c.m_max_sum += c.ncoeff(i); } } @@ -1858,7 +1617,7 @@ namespace smt { k.assert_expr(notB); lbool is_sat = k.check(); validating = false; - std::cout << is_sat << "\n"; + // std::cout << is_sat << "\n"; if (is_sat == l_true) { std::cout << A << "\n"; std::cout << B << "\n"; @@ -1978,82 +1737,6 @@ namespace smt { return true; } - void theory_pb::add_cardinality_lemma() { - context& ctx = get_context(); - normalize_active_coeffs(); - int s = 0; - int new_bound = 0; - if (!init_arg_max()) { - return; - } - // TBD: can be optimized - while (s < m_bound) { - int coeff; - int arg = arg_max(coeff); - if (arg == -1) break; - s += coeff; - ++new_bound; - } - int slack = m_active_coeffs.empty() ? m_bound : (std::min(m_bound, static_cast(m_active_coeffs[0]) - 1)); - reset_arg_max(); - - while (slack > 0) { - bool found = false; - int v = 0; - int coeff = 0; - for (unsigned i = 0; !found && i < m_active_vars.size(); ++i) { - bool_var v = m_active_vars[i]; - coeff = get_abs_coeff(v); - if (0 < coeff && coeff < slack) { - found = true; - } - } - if (!found) { - break; - } - slack -= coeff; - m_coeffs[v] = 0; // deactivate coefficient. - } - for (unsigned i = 0; i < m_active_vars.size(); ++i) { - bool_var v = m_active_vars[i]; - int coeff = get_coeff(v); - if (coeff < 0) { - m_coeffs[v] = -1; - } - else if (coeff > 0) { - m_coeffs[v] = 1; - } - } - - m_bound = new_bound; - if (!validate_lemma()) { - return; - } - SASSERT(m_bound > 0); - if (m_bound > static_cast(m_active_vars.size())) { - return; - } - if (m_bound == static_cast(m_active_vars.size())) { - return; - } - - m_antecedent_exprs.reset(); - m_antecedent_signs.reset(); - m_cardinality_exprs.reset(); - m_cardinality_signs.reset(); - for (unsigned i = 0; i < m_antecedents.size(); ++i) { - literal lit = m_antecedents[i]; - m_antecedent_exprs.push_back(ctx.bool_var2expr(lit.var())); - m_antecedent_signs.push_back(lit.sign()); - } - for (unsigned i = 0; i < m_active_vars.size(); ++i) { - bool_var v = m_active_vars[i]; - m_cardinality_exprs.push_back(ctx.bool_var2expr(v)); - m_cardinality_signs.push_back(get_coeff(v) < 0); - } - m_cardinality_lemma = true; - } - void theory_pb::normalize_active_coeffs() { while (!m_active_var_set.empty()) m_active_var_set.erase(); unsigned i = 0, j = 0, sz = m_active_vars.size(); @@ -2129,53 +1812,13 @@ namespace smt { m_coeffs[m_active_vars[i]] /= g; } m_bound = (m_bound + g - 1) / g; - std::cout << "CUT " << g << "\n"; TRACE("pb", display_resolved_lemma(tout << "cut\n");); } } - bool theory_pb::can_propagate() { return m_cardinality_lemma; } + bool theory_pb::can_propagate() { return false; } - void theory_pb::propagate() { - context& ctx = get_context(); - ast_manager& m = get_manager(); - if (!m_cardinality_lemma) { - return; - } - m_cardinality_lemma = false; - if (ctx.inconsistent()) { - return; - } - m_antecedents.reset(); - - for (unsigned i = 0; i < m_antecedent_exprs.size(); ++i) { - expr* a = m_antecedent_exprs[i].get(); - if (!ctx.b_internalized(a)) { - std::cout << "not internalized " << mk_pp(a, m) << "\n"; - return; - } - m_antecedents.push_back(~literal(ctx.get_bool_var(a), m_antecedent_signs[i])); - } - for (unsigned i = 0; i < m_cardinality_exprs.size(); ++i) { - expr* a = m_cardinality_exprs[i].get(); - if (!ctx.b_internalized(a)) { - std::cout << "not internalized " << mk_pp(a, m) << "\n"; - return; - } - if (m_cardinality_signs[i]) { - m_cardinality_exprs[i] = m.mk_not(a); - } - } - app_ref atl(pb.mk_at_least_k(m_cardinality_exprs.size(), m_cardinality_exprs.c_ptr(), m_bound), m); - VERIFY(internalize_card(atl, false)); - bool_var abv = ctx.get_bool_var(atl); - m_antecedents.push_back(literal(abv)); - justification* js = nullptr; - if (proofs_enabled()) { - js = nullptr; - } - ctx.mk_clause(m_antecedents.size(), m_antecedents.c_ptr(), js, CLS_AUX_LEMMA, nullptr); - } + void theory_pb::propagate() { } bool theory_pb::resolve_conflict(card& c, literal_vector const& confl) { @@ -2185,9 +1828,7 @@ namespace smt { context& ctx = get_context(); ast_manager& m = get_manager(); m_conflict_lvl = 0; - m_cardinality_lemma = false; - for (unsigned i = 0; i < confl.size(); ++i) { - literal lit = confl[i]; + for (literal lit : confl) { SASSERT(ctx.get_assignment(lit) == l_false); m_conflict_lvl = std::max(m_conflict_lvl, ctx.get_assign_level(lit)); } @@ -2195,8 +1836,6 @@ namespace smt { return false; } - // std::cout << c.lit() << "\n"; - reset_coeffs(); m_num_marks = 0; m_bound = c.k(); @@ -2285,6 +1924,7 @@ namespace smt { process_antecedent(~js.get_literal(), offset); break; case b_justification::AXIOM: + bound = 0; break; case b_justification::JUSTIFICATION: { justification* j = js.get_justification(); @@ -2295,7 +1935,11 @@ namespace smt { } if (pbj == nullptr) { TRACE("pb", tout << "skip justification for " << conseq << "\n";); - inc_coeff(conseq, offset); + bound = 0; + // this is possible when conseq is an assumption. + // The justification of conseq is itself, + // don't increment the cofficient here because it assumes + // conseq is justified further. it isnt'. conseq becomes part of the lemma. } else { card& c2 = pbj->get_card(); @@ -2303,7 +1947,6 @@ namespace smt { bound = c2.k(); } - // std::cout << " offset: " << offset << " bound: " << bound << "\n"; break; } default: @@ -2354,66 +1997,42 @@ namespace smt { slack += get_abs_coeff(v); } -#if 1 - //std::cout << slack << " " << m_bound << "\n"; unsigned i = 0; literal_vector const& alits = ctx.assigned_literals(); literal alit = get_asserting_literal(~conseq); slack -= get_abs_coeff(alit.var()); - for (i = alits.size(); 0 <= slack && i > 0; ) { - --i; + for (i = alits.size(); 0 <= slack && i-- > 0; ) { literal lit = alits[i]; bool_var v = lit.var(); // -3*x >= k if (m_active_var_set.contains(v) && v != alit.var()) { int coeff = get_coeff(v); - //std::cout << coeff << " " << lit << "\n"; if (coeff < 0 && !lit.sign()) { slack += coeff; m_antecedents.push_back(lit); - //std::cout << "ante: " << lit << "\n"; } else if (coeff > 0 && lit.sign()) { slack -= coeff; m_antecedents.push_back(lit); - //std::cout << "ante: " << lit << "\n"; } } } SASSERT(slack < 0); -#else - - literal alit = get_asserting_literal(~conseq); - slack -= get_abs_coeff(alit.var()); - - for (unsigned i = 0; 0 <= slack; ++i) { - SASSERT(i < m_active_vars.size()); - bool_var v = m_active_vars[i]; - literal lit(v, get_coeff(v) < 0); - if (v != alit.var() && ctx.get_assignment(lit) == l_false) { - m_antecedents.push_back(~lit); - slack -= get_abs_coeff(v); - } - if (slack < 0) { - std::cout << i << " " << m_active_vars.size() << "\n"; - } - } -#endif SASSERT(validate_antecedents(m_antecedents)); + TRACE("pb", tout << "antecedents " << m_antecedents << "\n";); ctx.assign(alit, ctx.mk_justification(theory_propagation_justification(get_id(), ctx.get_region(), m_antecedents.size(), m_antecedents.c_ptr(), alit, 0, nullptr))); DEBUG_CODE( m_antecedents.push_back(~alit); expr_ref_vector args(m); - for (unsigned i = 0; i < m_antecedents.size(); ++i) { - args.push_back(literal2expr(m_antecedents[i])); + for (literal lit : m_antecedents) { + args.push_back(literal2expr(lit)); } B = m.mk_not(m.mk_and(args.size(), args.c_ptr())); validate_implies(A, B); ); - // add_cardinality_lemma(); return true; } @@ -2477,21 +2096,58 @@ namespace smt { } void theory_pb::validate_final_check() { - for (unsigned i = 0; i < m_var_infos.size(); ++i) { - ineq* c = m_var_infos[i].m_ineq; - if (c) { - validate_final_check(*c); + TRACE("pb", tout << "validate " << m_var_infos.size() << "\n";); + for (auto & vi : m_var_infos) { + if (vi.m_ineq) { + validate_final_check(*vi.m_ineq); + } + if (vi.m_card) { + validate_final_check(*vi.m_card); } } } + void theory_pb::validate_final_check(card& c) { + context& ctx = get_context(); + if (ctx.get_assignment(c.lit()) == l_undef) { + TRACE("pb", display(tout << "is undef ", c, true);); + return; + } + if (!ctx.is_relevant(c.lit())) { + TRACE("pb", display(tout << "not relevant ", c, true);); + return; + } + + unsigned sum = 0, maxsum = 0; + for (unsigned i = 0; i < c.size(); ++i) { + switch(ctx.get_assignment(c.lit(i))) { + case l_true: + ++sum; + case l_undef: + ++maxsum; + break; + case l_false: + break; + } + } + TRACE("pb_verbose", display(tout << "validate: ", c, true); + tout << "sum: " << sum << " " << maxsum << " "; + tout << ctx.get_assignment(c.lit()) << "\n";); + + SASSERT(sum <= maxsum); + SASSERT((sum >= c.k()) == (ctx.get_assignment(c.lit()) == l_true)); + SASSERT((maxsum < c.k()) == (ctx.get_assignment(c.lit()) == l_false)); + } + void theory_pb::validate_final_check(ineq& c) { context& ctx = get_context(); if (ctx.get_assignment(c.lit()) == l_undef) { + TRACE("pb", tout << c.lit() << " is undef\n";); return; } if (!ctx.is_relevant(c.lit())) { + TRACE("pb", tout << c.lit() << " is not relevant\n";); return; } numeral sum = numeral::zero(), maxsum = numeral::zero(); @@ -2519,8 +2175,8 @@ namespace smt { bool theory_pb::validate_antecedents(literal_vector const& lits) { context& ctx = get_context(); - for (unsigned i = 0; i < lits.size(); ++i) { - if (ctx.get_assignment(lits[i]) != l_true) { + for (literal lit : lits) { + if (ctx.get_assignment(lit) != l_true) { return false; } } @@ -2530,7 +2186,9 @@ namespace smt { bool theory_pb::validate_unit_propagation(card const& c) { context& ctx = get_context(); for (unsigned i = c.k(); i < c.size(); ++i) { - VERIFY(ctx.get_assignment(c.lit(i)) == l_false); + if (ctx.get_assignment(c.lit(i)) != l_false) { + return false; + } } return true; } @@ -2560,15 +2218,11 @@ namespace smt { void theory_pb::display_resolved_lemma(std::ostream& out) const { context& ctx = get_context(); - bool_var v; - unsigned lvl; out << "num marks: " << m_num_marks << "\n"; out << "conflict level: " << m_conflict_lvl << "\n"; - for (unsigned i = 0; i < m_resolved.size(); ++i) { - v = m_resolved[i].var(); - lvl = ctx.get_assign_level(v); - out << lvl << ": " << m_resolved[i] << " "; - ctx.display(out, ctx.get_justification(v)); + for (literal r : m_resolved) { + out << ctx.get_assign_level(r) << ": " << r << " "; + ctx.display(out, ctx.get_justification(r.var())); } if (!m_antecedents.empty()) { @@ -2757,16 +2411,6 @@ namespace smt { display_watch(out, vi, false); display_watch(out, vi, true); } - for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) { - ineq_watch const* w = m_var_infos[vi].m_var_watch; - if (!w) continue; - out << "watch (v): " << literal(vi) << " |-> "; - ineq_watch const& wl = *w; - for (unsigned i = 0; i < wl.size(); ++i) { - out << wl[i]->lit() << " "; - } - out << "\n"; - } for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) { ineq* c = m_var_infos[vi].m_ineq; if (c) { diff --git a/src/smt/theory_pb.h b/src/smt/theory_pb.h index c20683d73..6b9b28100 100644 --- a/src/smt/theory_pb.h +++ b/src/smt/theory_pb.h @@ -97,9 +97,6 @@ namespace smt { unsigned m_num_conflicts; unsigned m_num_propagations; unsigned m_num_predicates; - unsigned m_num_compiles; - unsigned m_num_compiled_vars; - unsigned m_num_compiled_clauses; void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; @@ -120,8 +117,6 @@ namespace smt { scoped_mpz m_max_sum; // maximal possible sum. scoped_mpz m_min_sum; // minimal possible sum. unsigned m_num_propagations; - unsigned m_compilation_threshold; - lbool m_compiled; ineq(unsynch_mpz_manager& m, literal l, bool is_eq) : m_mpz(m), m_lit(l), m_is_eq(is_eq), @@ -197,8 +192,6 @@ namespace smt { unsigned m_bound; unsigned m_num_propagations; unsigned m_all_propagations; - unsigned m_compilation_threshold; - lbool m_compiled; bool m_aux; public: @@ -207,8 +200,6 @@ namespace smt { m_bound(bound), m_num_propagations(0), m_all_propagations(0), - m_compilation_threshold(0), - m_compiled(l_false), m_aux(is_aux) { SASSERT(bound > 0); @@ -252,13 +243,12 @@ namespace smt { struct var_info { ineq_watch* m_lit_watch[2]; - ineq_watch* m_var_watch; ineq* m_ineq; card_watch* m_lit_cwatch[2]; card* m_card; - var_info(): m_var_watch(nullptr), m_ineq(nullptr), m_card(nullptr) + var_info(): m_ineq(nullptr), m_card(nullptr) { m_lit_watch[0] = nullptr; m_lit_watch[1] = nullptr; @@ -269,7 +259,6 @@ namespace smt { void reset() { dealloc(m_lit_watch[0]); dealloc(m_lit_watch[1]); - dealloc(m_var_watch); dealloc(m_ineq); dealloc(m_lit_cwatch[0]); dealloc(m_lit_cwatch[1]); @@ -286,13 +275,9 @@ namespace smt { literal_vector m_literals; // temporary vector pb_util pb; stats m_stats; - ptr_vector m_to_compile; // inequalities to compile. unsigned m_conflict_frequency; bool m_learn_complements; - bool m_enable_compilation; - rational m_max_compiled_coeff; - bool m_cardinality_lemma; unsigned m_restart_lim; unsigned m_restart_inc; uint_set m_occs; @@ -305,16 +290,13 @@ namespace smt { void add_watch(ineq& c, unsigned index); void del_watch(ineq_watch& watch, unsigned index, ineq& c, unsigned ineq_index); void init_watch_literal(ineq& c); - void init_watch_var(ineq& c); + void init_watch_ineq(ineq& c); void clear_watch(ineq& c); void watch_literal(literal lit, ineq* c); - void watch_var(bool_var v, ineq* c); void unwatch_literal(literal w, ineq* c); - void unwatch_var(bool_var v, ineq* c); void remove(ptr_vector& ineqs, ineq* c); bool assign_watch_ge(bool_var v, bool is_true, ineq_watch& watch, unsigned index); - void assign_watch(bool_var v, bool is_true, ineq& c); void assign_ineq(ineq& c, bool is_true); void assign_eq(ineq& c, bool is_true); @@ -357,11 +339,6 @@ namespace smt { literal_vector& get_helpful_literals(ineq& c, bool negate); literal_vector& get_unhelpful_literals(ineq& c, bool negate); - // - // Utilities to compile cardinality - // constraints into a sorting network. - // - void compile_ineq(ineq& c); void inc_propagations(ineq& c); // @@ -396,7 +373,6 @@ namespace smt { void reset_arg_max(); void reset_coeffs(); - void add_cardinality_lemma(); literal get_asserting_literal(literal conseq); bool resolve_conflict(card& c, literal_vector const& conflict_clause); @@ -411,6 +387,7 @@ namespace smt { bool validate_lemma(); void validate_final_check(); void validate_final_check(ineq& c); + void validate_final_check(card& c); void validate_assign(ineq const& c, literal_vector const& lits, literal l) const; void validate_watch(ineq const& c) const; bool validate_unit_propagation(card const& c); diff --git a/src/smt/theory_recfun.h b/src/smt/theory_recfun.h index 46392c6d2..c233da059 100644 --- a/src/smt/theory_recfun.h +++ b/src/smt/theory_recfun.h @@ -69,7 +69,7 @@ namespace smt { recfun::case_def const * m_cdef; ptr_vector m_args; - body_expansion(recfun::util& u, app * n) : m_pred(n), m_cdef(0), m_args() { + body_expansion(recfun::util& u, app * n) : m_pred(n), m_cdef(nullptr), m_args() { m_cdef = &u.get_case_def(n); m_args.append(n->get_num_args(), n->get_args()); } @@ -125,7 +125,7 @@ namespace smt { literal mk_eq_lit(expr* l, expr* r); bool is_standard_order(recfun::vars const& vars) const { - return vars.size() == 0 || vars[vars.size()-1]->get_idx() == 0; + return vars.empty() || vars[vars.size()-1]->get_idx() == 0; } protected: void push_case_expand(case_expansion* e) { m_q_case_expand.push_back(e); } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 98192f3ae..72aea5909 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -51,6 +51,7 @@ public: m_kernel.assert_expr(e); lbool r = m_kernel.check(); m_kernel.pop(1); + IF_VERBOSE(11, verbose_stream() << "is " << r << " " << mk_pp(e, m_kernel.m()) << "\n"); return r; } }; @@ -208,11 +209,13 @@ theory_seq::theory_seq(ast_manager& m, theory_seq_params const & params): m_axioms(m), m_axioms_head(0), m_int_string(m), + m_length(m), m_mg(nullptr), m_rewrite(m), m_seq_rewrite(m), m_util(m), m_autil(m), + m_arith_value(m), m_trail_stack(*this), m_ls(m), m_rs(m), m_lhs(m), m_rhs(m), @@ -245,8 +248,12 @@ theory_seq::~theory_seq() { void theory_seq::init(context* ctx) { theory::init(ctx); + m_arith_value.init(ctx); } +#define TRACEFIN(s) { TRACE("seq", tout << ">>" << s << "\n";); IF_VERBOSE(11, verbose_stream() << s << "\n"); } + + final_check_status theory_seq::final_check_eh() { if (m_reset_cache) { m_rep.reset_cache(); @@ -255,81 +262,77 @@ final_check_status theory_seq::final_check_eh() { m_new_propagation = false; TRACE("seq", display(tout << "level: " << get_context().get_scope_level() << "\n");); TRACE("seq_verbose", get_context().display(tout);); + if (simplify_and_solve_eqs()) { ++m_stats.m_solve_eqs; - TRACE("seq", tout << ">>solve_eqs\n";); + TRACEFIN("solve_eqs"); return FC_CONTINUE; } if (check_contains()) { ++m_stats.m_propagate_contains; - TRACE("seq", tout << ">>propagate_contains\n";); + TRACEFIN("propagate_contains"); return FC_CONTINUE; } if (solve_nqs(0)) { ++m_stats.m_solve_nqs; - TRACE("seq", tout << ">>solve_nqs\n";); + TRACEFIN("solve_nqs"); return FC_CONTINUE; } if (fixed_length(true)) { ++m_stats.m_fixed_length; - TRACE("seq", tout << ">>zero_length\n";); + TRACEFIN("zero_length"); return FC_CONTINUE; } if (m_params.m_split_w_len && len_based_split()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>split_based_on_length\n";); + TRACEFIN("split_based_on_length"); return FC_CONTINUE; } if (fixed_length()) { ++m_stats.m_fixed_length; - TRACE("seq", tout << ">>fixed_length\n";); + TRACEFIN("fixed_length"); return FC_CONTINUE; } if (check_int_string()) { ++m_stats.m_int_string; - TRACE("seq", tout << ">>int_string\n";); + TRACEFIN("int_string"); return FC_CONTINUE; } if (reduce_length_eq()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>reduce length\n";); + TRACEFIN("reduce_length"); return FC_CONTINUE; } if (branch_unit_variable()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>branch_unit_variable\n";); + TRACEFIN("ranch_unit_variable"); return FC_CONTINUE; } if (branch_binary_variable()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>branch_binary_variable\n";); + TRACEFIN("branch_binary_variable"); return FC_CONTINUE; } - if (branch_ternary_variable1() || branch_ternary_variable2() || branch_quat_variable()) { + if (branch_variable()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>split_based_on_alignment\n";); - return FC_CONTINUE; - } - if (branch_variable_mb() || branch_variable()) { - ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>branch_variable\n";); + TRACEFIN("branch_variable"); return FC_CONTINUE; } if (check_length_coherence()) { ++m_stats.m_check_length_coherence; - TRACE("seq", tout << ">>check_length_coherence\n";); + TRACEFIN("check_length_coherence"); return FC_CONTINUE; } if (!check_extensionality()) { ++m_stats.m_extensionality; - TRACE("seq", tout << ">>extensionality\n";); + TRACEFIN("extensionality"); return FC_CONTINUE; } if (is_solved()) { - TRACE("seq", tout << ">>is_solved\n";); + TRACEFIN("is_solved"); return FC_DONE; } - TRACE("seq", tout << ">>give_up\n";); + TRACEFIN("give_up"); return FC_GIVEUP; } @@ -337,6 +340,7 @@ bool theory_seq::reduce_length_eq() { context& ctx = get_context(); int start = ctx.get_random_value(); + TRACE("seq", tout << "reduce length eq\n";); for (unsigned i = 0; !ctx.inconsistent() && i < m_eqs.size(); ++i) { eq const& e = m_eqs[(i + start) % m_eqs.size()]; if (reduce_length_eq(e.ls(), e.rs(), e.dep())) { @@ -380,7 +384,7 @@ bool theory_seq::branch_binary_variable(eq const& e) { rational lenX, lenY; context& ctx = get_context(); - if (branch_variable(e)) { + if (branch_variable_eq(e)) { return true; } if (!get_length(x, lenX)) { @@ -450,6 +454,9 @@ bool theory_seq::is_unit_eq(expr_ref_vector const& ls, expr_ref_vector const& rs if (ls.empty() || !is_var(ls[0])) { return false; } + //std::function is_unit = [&](expr* elem) { return m_util.str.is_unit(elem); } + //return rs.forall(is_unit); + for (auto const& elem : rs) { if (!m_util.str.is_unit(elem)) { return false; @@ -495,7 +502,9 @@ void theory_seq::branch_unit_variable(dependency* dep, expr* X, expr_ref_vector } bool theory_seq::branch_ternary_variable1() { - for (auto const& e : m_eqs) { + int start = get_context().get_random_value(); + for (unsigned i = 0; i < m_eqs.size(); ++i) { + eq const& e = m_eqs[(i + start) % m_eqs.size()]; if (branch_ternary_variable(e) || branch_ternary_variable2(e)) { return true; } @@ -504,7 +513,9 @@ bool theory_seq::branch_ternary_variable1() { } bool theory_seq::branch_ternary_variable2() { - for (auto const& e : m_eqs) { + int start = get_context().get_random_value(); + for (unsigned i = 0; i < m_eqs.size(); ++i) { + eq const& e = m_eqs[(i + start) % m_eqs.size()]; if (branch_ternary_variable(e, true)) { return true; } @@ -988,8 +999,9 @@ void theory_seq::find_max_eq_len(expr_ref_vector const& ls, expr_ref_vector cons hi = 1; } else { - lower_bound(ls.get(j), lo); - upper_bound(ls.get(j), hi); + expr_ref len_s = mk_len(ls.get(j)); + lower_bound(len_s, lo); + upper_bound(len_s, hi); } if (!lo.is_minus_one()) { if (lo1.is_minus_one()) @@ -1021,8 +1033,9 @@ void theory_seq::find_max_eq_len(expr_ref_vector const& ls, expr_ref_vector cons hi = 1; } else { - lower_bound(rs.get(j), lo); - upper_bound(rs.get(j), hi); + expr_ref len_s = mk_len(rs.get(j)); + lower_bound(len_s, lo); + upper_bound(len_s, hi); } if (!lo.is_minus_one()) { if (lo2.is_minus_one()) @@ -1085,73 +1098,53 @@ bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector cons expr* r_fst = find_fst_non_empty_var(rs); if (!r_fst) return false; expr_ref len_r_fst = mk_len(r_fst); + expr_ref len_l_fst(m); enode * root2; - if (!ctx.e_internalized(len_r_fst)) + if (!ctx.e_internalized(len_r_fst)) { return false; - else - root2 = get_root(len_r_fst); + } + if (l_fst) { + len_l_fst = mk_len(l_fst); + } + + root2 = get_root(len_r_fst); // Offset = 0, No change - if (l_fst) { - expr_ref len_l_fst = mk_len(l_fst); - if (ctx.e_internalized(len_l_fst)) { - enode * root1 = get_root(len_l_fst); - if (root1 == root2) { - TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); - return false; - } - } + if (l_fst && get_root(len_l_fst) == root2) { + TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); + return false; } // Offset = 0, Changed - { - for (unsigned i = 0; i < idx; ++i) { - eq const& e = m_eqs[i]; - if (e.ls().size() == ls.size()) { - bool flag = true; - for (unsigned j = 0; j < ls.size(); ++j) - if (e.ls().get(j) != ls.get(j)) { - flag = false; - break; - } - if (flag) { - expr* nl_fst = nullptr; - if (e.rs().size()>1 && is_var(e.rs().get(0))) - nl_fst = e.rs().get(0); - if (nl_fst && nl_fst != r_fst) { - expr_ref len_nl_fst = mk_len(nl_fst); - if (ctx.e_internalized(len_nl_fst)) { - enode * root1 = get_root(len_nl_fst); - if (root1 == root2) { - res.reset(); - res.append(e.rs().size(), e.rs().c_ptr()); - deps = m_dm.mk_join(e.dep(), deps); - return true; - } - } - } - } - } + + for (unsigned i = 0; i < idx; ++i) { + eq const& e = m_eqs[i]; + if (e.ls() != ls) continue; + expr* nl_fst = nullptr; + if (e.rs().size() > 1 && is_var(e.rs().get(0))) + nl_fst = e.rs().get(0); + if (nl_fst && nl_fst != r_fst && root2 == get_root(mk_len(nl_fst))) { + res.reset(); + res.append(e.rs().size(), e.rs().c_ptr()); + deps = m_dm.mk_join(e.dep(), deps); + return true; } } // Offset != 0, No change - if (l_fst) { - expr_ref len_l_fst = mk_len(l_fst); - if (ctx.e_internalized(len_l_fst)) { - enode * root1 = get_root(len_l_fst); - obj_map tmp; - int offset; - if (!m_autil.is_numeral(root1->get_owner()) && !m_autil.is_numeral(root2->get_owner())) { - if (m_len_offset.find(root1, tmp) && tmp.find(root2, offset)) { - TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); - find_max_eq_len(ls, rs); - return false; - } - else if (m_len_offset.find(root2, tmp) && tmp.find(root1, offset)) { - TRACE("seq", tout << "(" << mk_pp(r_fst, m) << ", " << mk_pp(l_fst,m) << ")\n";); - find_max_eq_len(ls ,rs); - return false; - } + if (l_fst && ctx.e_internalized(len_l_fst)) { + enode * root1 = get_root(len_l_fst); + obj_map tmp; + int offset; + if (!m_autil.is_numeral(root1->get_owner()) && !m_autil.is_numeral(root2->get_owner())) { + if (m_len_offset.find(root1, tmp) && tmp.find(root2, offset)) { + TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); + find_max_eq_len(ls, rs); + return false; + } + else if (m_len_offset.find(root2, tmp) && tmp.find(root1, offset)) { + TRACE("seq", tout << "(" << mk_pp(r_fst, m) << ", " << mk_pp(l_fst,m) << ")\n";); + find_max_eq_len(ls ,rs); + return false; } } } @@ -1160,30 +1153,21 @@ bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector cons if (!m_autil.is_numeral(root2->get_owner()) && m_len_offset.find(root2, tmp)) { for (unsigned i = 0; i < idx; ++i) { eq const& e = m_eqs[i]; - if (e.ls().size() == ls.size()) { - bool flag = true; - for (unsigned j = 0; j < ls.size(); ++j) - if (e.ls().get(j) != ls.get(j)) { - flag = false; - break; - } - if (flag) { - expr* nl_fst = nullptr; - if (e.rs().size()>1 && is_var(e.rs().get(0))) - nl_fst = e.rs().get(0); - if (nl_fst && nl_fst != r_fst) { - int offset; - expr_ref len_nl_fst = mk_len(nl_fst); - if (ctx.e_internalized(len_nl_fst)) { - enode * root1 = ctx.get_enode(len_nl_fst)->get_root(); - if (!m_autil.is_numeral(root1->get_owner()) && tmp.find(root1, offset)) { - res.reset(); - res.append(e.rs().size(), e.rs().c_ptr()); - deps = m_dm.mk_join(e.dep(), deps); - find_max_eq_len(res, rs); - return true; - } - } + if (e.ls() != ls) continue; + expr* nl_fst = nullptr; + if (e.rs().size()>1 && is_var(e.rs().get(0))) + nl_fst = e.rs().get(0); + if (nl_fst && nl_fst != r_fst) { + int offset; + expr_ref len_nl_fst = mk_len(nl_fst); + if (ctx.e_internalized(len_nl_fst)) { + enode * root1 = ctx.get_enode(len_nl_fst)->get_root(); + if (!m_autil.is_numeral(root1->get_owner()) && tmp.find(root1, offset)) { + res.reset(); + res.append(e.rs().size(), e.rs().c_ptr()); + deps = m_dm.mk_join(e.dep(), deps); + find_max_eq_len(res, rs); + return true; } } } @@ -1195,40 +1179,41 @@ bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector cons bool theory_seq::has_len_offset(expr_ref_vector const& ls, expr_ref_vector const& rs, int & offset) { context& ctx = get_context(); - if (ls.size() == 0 || rs.size() == 0) + if (ls.empty() || rs.empty()) return false; expr* l_fst = ls[0]; expr* r_fst = rs[0]; if (!is_var(l_fst) || !is_var(r_fst)) return false; + expr_ref len_l_fst = mk_len(l_fst); + if (!ctx.e_internalized(len_l_fst)) + return false; + enode * root1 = ctx.get_enode(len_l_fst)->get_root(); + expr_ref len_r_fst = mk_len(r_fst); - enode * root2; if (!ctx.e_internalized(len_r_fst)) return false; - else - root2 = ctx.get_enode(len_r_fst)->get_root(); + enode* root2 = ctx.get_enode(len_r_fst)->get_root(); - expr_ref len_l_fst = mk_len(l_fst); - if (ctx.e_internalized(len_l_fst)) { - enode * root1 = ctx.get_enode(len_l_fst)->get_root(); - if (root1 == root2) { - TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); - offset = 0; - return true; - } - obj_map tmp; - if (!m_autil.is_numeral(root1->get_owner()) && !m_autil.is_numeral(root2->get_owner())) { - if (m_len_offset.find(root1, tmp) && tmp.find(root2, offset)) { - TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); - return true; - } - else if (m_len_offset.find(root2, tmp) && tmp.find(root1, offset)) { - offset = -offset; - TRACE("seq", tout << "(" << mk_pp(r_fst, m) << ", " << mk_pp(l_fst,m) << ")\n";); - return true; - } - } + if (root1 == root2) { + TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); + offset = 0; + return true; + } + + if (m_autil.is_numeral(root1->get_owner()) || m_autil.is_numeral(root2->get_owner())) + return false; + + obj_map tmp; + if (m_len_offset.find(root1, tmp) && tmp.find(root2, offset)) { + TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); + return true; + } + if (m_len_offset.find(root2, tmp) && tmp.find(root1, offset)) { + offset = -offset; + TRACE("seq", tout << "(" << mk_pp(r_fst, m) << ", " << mk_pp(l_fst,m) << ")\n";); + return true; } return false; } @@ -1355,6 +1340,20 @@ bool theory_seq::len_based_split(eq const& e) { return true; } +/** + \brief select branching on variable equality. + preference mb > eq > ternary > quat + this performs much better on #1628 +*/ +bool theory_seq::branch_variable() { + if (branch_variable_mb()) return true; + if (branch_variable_eq()) return true; + if (branch_ternary_variable1()) return true; + if (branch_ternary_variable2()) return true; + if (branch_quat_variable()) return true; + return false; +} + bool theory_seq::branch_variable_mb() { bool change = false; for (auto const& e : m_eqs) { @@ -1512,8 +1511,7 @@ bool theory_seq::enforce_length(expr_ref_vector const& es, vector & le bool all_have_length = true; rational val; zstring s; - for (unsigned i = 0; i < es.size(); ++i) { - expr* e = es[i]; + for (expr* e : es) { if (m_util.str.is_unit(e)) { len.push_back(rational(1)); } @@ -1534,7 +1532,7 @@ bool theory_seq::enforce_length(expr_ref_vector const& es, vector & le return all_have_length; } -bool theory_seq::branch_variable() { +bool theory_seq::branch_variable_eq() { context& ctx = get_context(); unsigned sz = m_eqs.size(); int start = ctx.get_random_value(); @@ -1543,24 +1541,15 @@ bool theory_seq::branch_variable() { unsigned k = (i + start) % sz; eq const& e = m_eqs[k]; - if (branch_variable(e)) { + if (branch_variable_eq(e)) { TRACE("seq", tout << "branch variable\n";); return true; } - -#if 0 - if (!has_length(e.ls())) { - enforce_length(e.ls()); - } - if (!has_length(e.rs())) { - enforce_length(e.rs()); - } -#endif } return ctx.inconsistent(); } -bool theory_seq::branch_variable(eq const& e) { +bool theory_seq::branch_variable_eq(eq const& e) { unsigned id = e.id(); unsigned s = find_branch_start(2*id); TRACE("seq", tout << s << " " << id << ": " << e.ls() << " = " << e.rs() << "\n";); @@ -1755,7 +1744,7 @@ bool theory_seq::propagate_length_coherence(expr* e) { } TRACE("seq", tout << "Unsolved " << mk_pp(e, m); if (!lower_bound2(e, lo)) lo = -rational::one(); - if (!upper_bound(e, hi)) hi = -rational::one(); + if (!upper_bound(mk_len(e), hi)) hi = -rational::one(); tout << " lo: " << lo << " hi: " << hi << "\n"; ); @@ -1773,9 +1762,10 @@ bool theory_seq::propagate_length_coherence(expr* e) { // len(e) >= low => e = tail; literal low(mk_literal(m_autil.mk_ge(mk_len(e), m_autil.mk_numeral(lo, true)))); add_axiom(~low, mk_seq_eq(e, tail)); - if (upper_bound(e, hi)) { + expr_ref len_e = mk_len(e); + if (upper_bound(len_e, hi)) { // len(e) <= hi => len(tail) <= hi - lo - expr_ref high1(m_autil.mk_le(mk_len(e), m_autil.mk_numeral(hi, true)), m); + expr_ref high1(m_autil.mk_le(len_e, m_autil.mk_numeral(hi, true)), m); if (hi == lo) { add_axiom(~mk_literal(high1), mk_seq_eq(seq, emp)); } @@ -1825,13 +1815,17 @@ bool theory_seq::check_length_coherence0(expr* e) { bool theory_seq::check_length_coherence() { #if 1 - for (auto e : m_length) { + for (expr* l : m_length) { + expr* e = nullptr; + VERIFY(m_util.str.is_length(l, e)); if (check_length_coherence0(e)) { return true; } } #endif - for (auto e : m_length) { + for (expr* l : m_length) { + expr* e = nullptr; + VERIFY(m_util.str.is_length(l, e)); if (check_length_coherence(e)) { return true; } @@ -1849,9 +1843,11 @@ bool theory_seq::fixed_length(bool is_zero) { return found; } -bool theory_seq::fixed_length(expr* e, bool is_zero) { +bool theory_seq::fixed_length(expr* len_e, bool is_zero) { rational lo, hi; - if (!(is_var(e) && lower_bound(e, lo) && upper_bound(e, hi) && lo == hi + expr* e = nullptr; + VERIFY(m_util.str.is_length(len_e, e)); + if (!(is_var(e) && lower_bound(len_e, lo) && upper_bound(len_e, hi) && lo == hi && ((is_zero && lo.is_zero()) || (!is_zero && lo.is_unsigned())))) { return false; } @@ -1884,9 +1880,9 @@ bool theory_seq::fixed_length(expr* e, bool is_zero) { seq = mk_concat(elems.size(), elems.c_ptr()); } TRACE("seq", tout << "Fixed: " << mk_pp(e, m) << " " << lo << "\n";); - add_axiom(~mk_eq(mk_len(e), m_autil.mk_numeral(lo, true), false), mk_seq_eq(seq, e)); + add_axiom(~mk_eq(len_e, m_autil.mk_numeral(lo, true), false), mk_seq_eq(seq, e)); if (!ctx.at_base_level()) { - m_trail_stack.push(push_replay(alloc(replay_fixed_length, m, e))); + m_trail_stack.push(push_replay(alloc(replay_fixed_length, m, len_e))); } return true; } @@ -2159,6 +2155,7 @@ void theory_seq::propagate_lit(dependency* dep, unsigned n, literal const* _lits if (!linearize(dep, eqs, lits)) return; TRACE("seq", + tout << "scope: " << ctx.get_scope_level() << "\n"; ctx.display_detailed_literal(tout << "assert:", lit); ctx.display_literals_verbose(tout << " <- ", lits); if (!lits.empty()) tout << "\n"; display_deps(tout, dep);); @@ -2700,8 +2697,8 @@ bool theory_seq::reduce_length_eq(expr_ref_vector const& ls, expr_ref_vector con rhs.append(rs.size()-1, rs.c_ptr()); SASSERT(!lhs.empty() || !rhs.empty()); deps = mk_join(deps, lits); + TRACE("seq", tout << "Propagate equal lengths " << l << " " << r << "\n" << "ls: " << ls << "\nrs: " << rs << "\n";); m_eqs.push_back(eq(m_eq_id++, lhs, rhs, deps)); - TRACE("seq", tout << "Propagate equal lengths " << l << " " << r << "\n";); propagate_eq(deps, lits, l, r, true); return true; } @@ -2774,10 +2771,6 @@ bool theory_seq::reduce_length(unsigned i, unsigned j, bool front, expr_ref_vect expr_ref lenr = mk_len(r); literal lit = mk_eq(lenl, lenr, false); if (ctx.get_assignment(lit) == l_true) { -// expr_ref len_eq(m.mk_eq(lenl, lenr), m); -// if (ctx.find_assignment(len_eq) == l_true) { -// literal lit = mk_eq(lenl, lenr, false); -// literal_vector lits; expr_ref_vector lhs(m), rhs(m); lhs.append(l2, ls2); rhs.append(r2, rs2); @@ -2788,7 +2781,6 @@ bool theory_seq::reduce_length(unsigned i, unsigned j, bool front, expr_ref_vect return true; } else { - //TRACE("seq", tout << "Assignment: " << lenl << " = " << lenr << " " << ctx.get_assignment(lit) << "\n";); return false; } } @@ -2889,9 +2881,9 @@ bool theory_seq::get_length(expr* e, expr_ref& len, literal_vector& lits) { ctx.get_assignment(i_lt_len_s) == l_true) { len = m_autil.mk_int(1); lits.append(2, _lits); + TRACE("seq", ctx.display_literals_verbose(tout, 2, _lits); tout << "\n";); return true; } - TRACE("seq", ctx.display_literals_verbose(tout, 2, _lits); tout << "\n";); } else if (is_pre(e, s, i)) { expr_ref zero(m_autil.mk_int(0), m); @@ -2903,9 +2895,9 @@ bool theory_seq::get_length(expr* e, expr_ref& len, literal_vector& lits) { ctx.get_assignment(i_lt_len_s) == l_true) { len = i; lits.append(2, _lits); + TRACE("seq", ctx.display_literals_verbose(tout, 2, _lits); tout << "\n";); return true; } - TRACE("seq", ctx.display_literals_verbose(tout, 2, _lits); tout << "\n";); } else if (is_post(e, s, l)) { expr_ref zero(m_autil.mk_int(0), m); @@ -2916,19 +2908,31 @@ bool theory_seq::get_length(expr* e, expr_ref& len, literal_vector& lits) { ctx.get_assignment(l_le_len_s) == l_true) { len = l; lits.append(2, _lits); + TRACE("seq", ctx.display_literals_verbose(tout, 2, _lits); tout << "\n";); return true; } - TRACE("seq", ctx.display_literals_verbose(tout, 2, _lits); tout << "\n";); } else if (is_skolem(m_tail, e)) { + // e = tail(s, l), len(s) > l => len(tail(s, l)) = len(s) - l - 1 + // e = tail(s, l), len(s) <= l => len(tail(s, l)) = 0 + s = to_app(e)->get_arg(0); l = to_app(e)->get_arg(1); expr_ref len_s = mk_len(s); - literal len_s_ge_l = mk_simplified_literal(m_autil.mk_ge(mk_sub(len_s, l), m_autil.mk_int(0))); - if (ctx.get_assignment(len_s_ge_l) == l_true) { - len = mk_sub(len_s, l); - lits.push_back(len_s_ge_l); + literal len_s_gt_l = mk_simplified_literal(m_autil.mk_ge(mk_sub(len_s, l), m_autil.mk_int(1))); + switch (ctx.get_assignment(len_s_gt_l)) { + case l_true: + len = mk_sub(len_s, mk_sub(l, m_autil.mk_int(1))); + TRACE("seq", tout << len_s << " " << len << " " << len_s_gt_l << "\n";); + lits.push_back(len_s_gt_l); return true; + case l_false: + len = m_autil.mk_int(0); + TRACE("seq", tout << len_s << " " << len << " " << len_s_gt_l << "\n";); + lits.push_back(~len_s_gt_l); + return true; + default: + break; } } else if (m_util.str.is_unit(e)) { @@ -3160,6 +3164,9 @@ bool theory_seq::solve_nc(unsigned idx) { if (!linearize(deps, eqs, lits)) { return false; } + for (literal& lit : lits) { + lit.neg(); + } for (enode_pair const& p : eqs) { lits.push_back(~mk_eq(p.first->get_owner(), p.second->get_owner(), false)); } @@ -3397,10 +3404,14 @@ bool theory_seq::internalize_term(app* term) { return true; } -void theory_seq::add_length(expr* e) { - SASSERT(!has_length(e)); - m_length.insert(e); - m_trail_stack.push(insert_obj_trail(m_length, e)); +void theory_seq::add_length(expr* l) { + expr* e = nullptr; + VERIFY(m_util.str.is_length(l, e)); + SASSERT(!m_length.contains(l)); + m_length.push_back(l); + m_has_length.insert(e); + m_trail_stack.push(insert_obj_trail(m_has_length, e)); + m_trail_stack.push(push_back_vector(m_length)); } @@ -3415,7 +3426,7 @@ void theory_seq::enforce_length(expr* e) { if (!has_length(o)) { expr_ref len = mk_len(o); enque_axiom(len); - add_length(o); + add_length(len); } n = n->get_next(); } @@ -3440,6 +3451,7 @@ bool theory_seq::check_int_string() { bool theory_seq::check_int_string(expr* e) { return + get_context().inconsistent() || (m_util.str.is_itos(e) && add_itos_val_axiom(e)) || (m_util.str.is_stoi(e) && add_stoi_val_axiom(e)); } @@ -3453,81 +3465,9 @@ void theory_seq::add_stoi_axiom(expr* e) { // stoi(s) >= -1 literal l = mk_simplified_literal(m_autil.mk_ge(e, m_autil.mk_int(-1))); add_axiom(l); - -} -void theory_seq::ensure_digit_axiom() { - - if (m_si_axioms.empty()) { - bv_util bv(m); - for (unsigned i = 0; i < 10; ++i) { - expr_ref cnst(bv.mk_numeral(rational('0'+i), bv.mk_sort(8)), m); - add_axiom(mk_eq(digit2int(cnst), m_autil.mk_int(i), false)); - } - } -} - -bool theory_seq::add_itos_val_axiom(expr* e) { - rational val; - expr* n = nullptr; - TRACE("seq", tout << mk_pp(e, m) << "\n";); - VERIFY(m_util.str.is_itos(e, n)); - - if (m_util.str.is_stoi(n)) { - return false; - } - enforce_length(e); - - if (get_length(e, val) && val.is_pos() && val.is_unsigned() && !m_si_axioms.contains(e)) { - add_si_axiom(e, n, val.get_unsigned()); - m_si_axioms.insert(e); - m_trail_stack.push(push_replay(alloc(replay_is_axiom, m, e))); - m_trail_stack.push(insert_map, expr*>(m_si_axioms, e)); - return true; - } - - return false; -} - -bool theory_seq::add_stoi_val_axiom(expr* e) { - context& ctx = get_context(); - expr* n = nullptr; - rational val; - TRACE("seq", tout << mk_pp(e, m) << " " << ctx.get_scope_level () << "\n";); - VERIFY(m_util.str.is_stoi(e, n)); - - if (m_util.str.is_itos(n)) { - return false; - } - enforce_length(n); - - if (get_length(n, val) && val.is_pos() && val.is_unsigned() && !m_si_axioms.contains(e)) { - add_si_axiom(n, e, val.get_unsigned()); - m_si_axioms.insert(e); - m_trail_stack.push(push_replay(alloc(replay_is_axiom, m, e))); - m_trail_stack.push(insert_map, expr*>(m_si_axioms, e)); - return true; - } - - return false; -} - -literal theory_seq::is_digit(expr* ch) { - bv_util bv(m); - literal isd = mk_literal(mk_skolem(symbol("seq.is_digit"), ch, nullptr, nullptr, nullptr, m.mk_bool_sort())); - expr_ref d2i = digit2int(ch); - expr_ref _lo(bv.mk_ule(bv.mk_numeral(rational('0'), bv.mk_sort(8)), ch), m); - expr_ref _hi(bv.mk_ule(ch, bv.mk_numeral(rational('9'), bv.mk_sort(8))), m); - literal lo = mk_literal(_lo); - literal hi = mk_literal(_hi); - add_axiom(~lo, ~hi, isd); - add_axiom(~isd, lo); - add_axiom(~isd, hi); - return isd; -} - -expr_ref theory_seq::digit2int(expr* ch) { - return expr_ref(mk_skolem(symbol("seq.digit2int"), ch, nullptr, nullptr, nullptr, m_autil.mk_int()), m); + // stoi("") = -1 + add_axiom(mk_eq(m_util.str.mk_stoi(m_util.str.mk_empty(m.get_sort(s))), m_autil.mk_int(-1), false)); } void theory_seq::add_itos_axiom(expr* e) { @@ -3537,7 +3477,7 @@ void theory_seq::add_itos_axiom(expr* e) { VERIFY(m_util.str.is_itos(e, n)); // itos(n) = "" <=> n < 0 - expr_ref zero(arith_util(m).mk_int(0), m); + expr_ref zero(m_autil.mk_int(0), m); literal eq1 = mk_literal(m_util.str.mk_is_empty(e)); literal ge0 = mk_literal(m_autil.mk_ge(n, zero)); // n >= 0 => itos(n) != "" @@ -3552,7 +3492,81 @@ void theory_seq::add_itos_axiom(expr* e) { } -// n >= 0 & len(e) = k => is_digit(e_i) for i = 0..k-1 +void theory_seq::ensure_digit_axiom() { + + if (m_si_axioms.empty()) { + for (unsigned i = 0; i < 10; ++i) { + expr_ref cnst(m_util.mk_char('0'+i), m); + add_axiom(mk_eq(digit2int(cnst), m_autil.mk_int(i), false)); + } + } +} + +bool theory_seq::add_itos_val_axiom(expr* e) { + rational val, val2; + expr* n = nullptr; + TRACE("seq", tout << mk_pp(e, m) << "\n";); + VERIFY(m_util.str.is_itos(e, n)); + + if (m_util.str.is_stoi(n)) { + return false; + } + enforce_length(e); + + if (get_length(e, val) && val.is_pos() && val.is_unsigned() && (!m_si_axioms.find(e, val2) || val != val2)) { + add_si_axiom(e, n, val.get_unsigned()); + m_si_axioms.insert(e, val); + m_trail_stack.push(push_replay(alloc(replay_is_axiom, m, e))); + m_trail_stack.push(insert_map, expr*>(m_si_axioms, e)); + return true; + } + + return false; +} + +bool theory_seq::add_stoi_val_axiom(expr* e) { + expr* n = nullptr; + rational val, val2; + VERIFY(m_util.str.is_stoi(e, n)); + + TRACE("seq", tout << mk_pp(e, m) << " " << get_context().get_scope_level () << " " << get_length(n, val) << " " << val << "\n";); + + if (m_util.str.is_itos(n)) { + return false; + } + enforce_length(n); + + if (get_length(n, val) && val.is_pos() && val.is_unsigned() && (!m_si_axioms.find(e, val2) || val2 != val)) { + add_si_axiom(n, e, val.get_unsigned()); + m_si_axioms.insert(e, val); + m_trail_stack.push(push_replay(alloc(replay_is_axiom, m, e))); + m_trail_stack.push(insert_map, expr*>(m_si_axioms, e)); + return true; + } + + return false; +} + +literal theory_seq::is_digit(expr* ch) { + literal isd = mk_literal(mk_skolem(symbol("seq.is_digit"), ch, nullptr, nullptr, nullptr, m.mk_bool_sort())); + expr_ref d2i = digit2int(ch); + expr_ref _lo(m_util.mk_le(m_util.mk_char('0'), ch), m); + expr_ref _hi(m_util.mk_le(ch, m_util.mk_char('9')), m); + literal lo = mk_literal(_lo); + literal hi = mk_literal(_hi); + add_axiom(~lo, ~hi, isd); + add_axiom(~isd, lo); + add_axiom(~isd, hi); + return isd; +} + +expr_ref theory_seq::digit2int(expr* ch) { + return expr_ref(mk_skolem(symbol("seq.digit2int"), ch, nullptr, nullptr, nullptr, m_autil.mk_int()), m); +} + + + +// n >= 0 & len(e) >= i + 1 => is_digit(e_i) for i = 0..k-1 // n >= 0 & len(e) = k => n = sum 10^i*digit(e_i) // n < 0 & len(e) = k => \/_i ~is_digit(e_i) for i = 0..k-1 // 10^k <= n < 10^{k+1}-1 => len(e) = k @@ -3572,7 +3586,9 @@ void theory_seq::add_si_axiom(expr* e, expr* n, unsigned k) { for (unsigned i = 0; i < k; ++i) { ith_char = mk_nth(e, m_autil.mk_int(i)); literal isd = is_digit(ith_char); - add_axiom(~len_eq_k, ~ge0, isd); + literal len_ge_i1 = mk_literal(m_autil.mk_ge(len, m_autil.mk_int(i+1))); + add_axiom(~len_ge_i1, ~ge0, isd); + digits.push_back(~isd); chars.push_back(m_util.str.mk_unit(ith_char)); nums.push_back(digit2int(ith_char)); } @@ -3593,7 +3609,7 @@ void theory_seq::add_si_axiom(expr* e, expr* n, unsigned k) { SASSERT(k > 0); rational lb = power(rational(10), k - 1); rational ub = power(rational(10), k) - 1; - arith_util a (m); + arith_util& a = m_autil; literal lbl = mk_literal(a.mk_ge(n, a.mk_int(lb))); literal ubl = mk_literal(a.mk_le(n, a.mk_int(ub))); literal ge_k = mk_literal(a.mk_ge(len, a.mk_int(k))); @@ -3613,18 +3629,18 @@ void theory_seq::apply_sort_cnstr(enode* n, sort* s) { } void theory_seq::display(std::ostream & out) const { - if (m_eqs.size() == 0 && - m_nqs.size() == 0 && + if (m_eqs.empty() && + m_nqs.empty() && m_rep.empty() && m_exclude.empty()) { return; } out << "Theory seq\n"; - if (m_eqs.size() > 0) { + if (!m_eqs.empty()) { out << "Equations:\n"; display_equations(out); } - if (m_nqs.size() > 0) { + if (!m_nqs.empty()) { display_disequations(out); } if (!m_re2aut.empty()) { @@ -3646,14 +3662,12 @@ void theory_seq::display(std::ostream & out) const { m_exclude.display(out); } - if (!m_length.empty()) { - for (auto e : m_length) { - rational lo(-1), hi(-1); - lower_bound(e, lo); - upper_bound(e, hi); - if (lo.is_pos() || !hi.is_minus_one()) { - out << mk_pp(e, m) << " [" << lo << ":" << hi << "]\n"; - } + for (auto e : m_length) { + rational lo(-1), hi(-1); + lower_bound(e, lo); + upper_bound(e, hi); + if (lo.is_pos() || !hi.is_minus_one()) { + out << mk_pp(e, m) << " [" << lo << ":" << hi << "]\n"; } } @@ -3698,7 +3712,7 @@ std::ostream& theory_seq::display_disequation(std::ostream& out, ne const& e) co for (literal lit : e.lits()) { out << lit << " "; } - if (e.lits().size() > 0) { + if (!e.lits().empty()) { out << "\n"; } for (unsigned j = 0; j < e.ls().size(); ++j) { @@ -3762,6 +3776,7 @@ void theory_seq::collect_statistics(::statistics & st) const { st.update("seq extensionality", m_stats.m_extensionality); st.update("seq fixed length", m_stats.m_fixed_length); st.update("seq int.to.str", m_stats.m_int_string); + st.update("seq automata", m_stats.m_propagate_automata); } void theory_seq::init_search_eh() { @@ -3840,18 +3855,17 @@ public: SASSERT(values.size() == m_dependencies.size()); expr_ref_vector args(th.m); unsigned j = 0, k = 0; + rational val; bool is_string = th.m_util.is_string(m_sort); expr_ref result(th.m); if (is_string) { unsigned_vector sbuffer; - bv_util bv(th.m); - rational val; - unsigned sz; + unsigned ch; for (source_t src : m_source) { switch (src) { case unit_source: { - VERIFY(bv.is_numeral(values[j++], val, sz)); - sbuffer.push_back(val.get_unsigned()); + VERIFY(th.m_util.is_const_char(values[j++], ch)); + sbuffer.push_back(ch); break; } case string_source: { @@ -3905,28 +3919,35 @@ public: th.m_rewrite(result); } th.m_factory->add_trail(result); + TRACE("seq", tout << result << "\n";); return to_app(result); } }; +app* theory_seq::get_ite_value(expr* e) { + expr* e1, *e2, *e3; + while (m.is_ite(e, e1, e2, e3)) { + if (get_root(e2) == get_root(e)) { + e = e2; + } + else if (get_root(e3) == get_root(e)) { + e = e3; + } + else { + break; + } + } + return to_app(e); +} model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) { app* e = n->get_owner(); context& ctx = get_context(); - expr* e1, *e2, *e3; - if (m.is_ite(e, e1, e2, e3) && ctx.e_internalized(e2) && ctx.e_internalized(e3) && - (get_root(e2) == n->get_root() || - get_root(e3) == n->get_root())) { - if (ctx.get_enode(e2)->get_root() == n->get_root()) { - return mk_value(ctx.get_enode(e2), mg); - } - else { - return mk_value(ctx.get_enode(e3), mg); - } - } - else if (m_util.is_seq(e)) { + TRACE("seq", tout << mk_pp(n->get_owner(), m) << "\n";); + e = get_ite_value(e); + if (m_util.is_seq(e)) { ptr_vector concats; - get_concat(e, concats); + get_ite_concat(e, concats); sort* srt = m.get_sort(e); seq_value_proc* sv = alloc(seq_value_proc, *this, srt); @@ -3961,7 +3982,9 @@ model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) { app* theory_seq::mk_value(app* e) { expr_ref result(m); + e = get_ite_value(e); result = m_rep.find(e); + if (is_var(result)) { SASSERT(m_factory); expr_ref val(m); @@ -4252,7 +4275,7 @@ void theory_seq::deque_axiom(expr* n) { if (m_util.str.is_length(n)) { add_length_axiom(n); } - else if (m_util.str.is_empty(n) && !has_length(n) && !m_length.empty()) { + else if (m_util.str.is_empty(n) && !has_length(n) && !m_has_length.empty()) { enforce_length(n); } else if (m_util.str.is_index(n)) { @@ -4267,6 +4290,9 @@ void theory_seq::deque_axiom(expr* n) { else if (m_util.str.is_at(n)) { add_at_axiom(n); } + else if (m_util.str.is_nth(n)) { + add_nth_axiom(n); + } else if (m_util.str.is_string(n)) { add_elim_string_axiom(n); } @@ -4478,7 +4504,7 @@ void theory_seq::add_length_axiom(expr* n) { } else if (m_util.str.is_itos(x)) { add_itos_length_axiom(n); - } + } else { add_axiom(mk_literal(m_autil.mk_ge(n, m_autil.mk_int(0)))); } @@ -4543,8 +4569,6 @@ void theory_seq::add_itos_length_axiom(expr* len) { void theory_seq::propagate_in_re(expr* n, bool is_true) { TRACE("seq", tout << mk_pp(n, m) << " <- " << (is_true?"true":"false") << "\n";); - expr* s = nullptr, *re = nullptr; - VERIFY(m_util.str.is_in_re(n, s, re)); expr_ref tmp(n, m); m_rewrite(tmp); @@ -4565,48 +4589,44 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { return; } - expr_ref e3(re, m); + expr* s = nullptr, *_re = nullptr; + VERIFY(m_util.str.is_in_re(n, s, _re)); + expr_ref re(_re, m); context& ctx = get_context(); literal lit = ctx.get_literal(n); if (!is_true) { - e3 = m_util.re.mk_complement(re); + re = m_util.re.mk_complement(re); lit.neg(); } - literal_vector lits; - - enode_pair_vector eqs; + literal_vector lits; for (unsigned i = 0; i < m_s_in_re.size(); ++i) { auto const& entry = m_s_in_re[i]; - if (entry.m_active && get_root(entry.m_s) == get_root(s)) { + if (entry.m_active && get_root(entry.m_s) == get_root(s) && entry.m_re != re) { m_trail_stack.push(vector_value_trail(m_s_in_re, i)); m_s_in_re[i].m_active = false; - e3 = m_util.re.mk_inter(entry.m_re, e3); - lits.push_back(entry.m_lit); - eqs.push_back(enode_pair(ensure_enode(entry.m_s), ensure_enode(s))); + IF_VERBOSE(11, verbose_stream() << "intersect " << re << " " << mk_pp(entry.m_re, m) << " " << mk_pp(s, m) << " " << mk_pp(entry.m_s, m) << "\n";); + re = m_util.re.mk_inter(entry.m_re, re); + m_rewrite(re); + lits.push_back(~entry.m_lit); + enode* n1 = ensure_enode(entry.m_s); + enode* n2 = ensure_enode(s); + if (n1 != n2) { + lits.push_back(~mk_eq(n1->get_owner(), n2->get_owner(), false)); + } } } - if (!lits.empty()) { - TRACE("seq", tout << "creating intersection " << e3 << "\n";); - lits.push_back(lit); - expr* e = m_util.re.mk_in_re(s, e3); - literal inter = mk_literal(e); - justification* js = - ctx.mk_justification( - ext_theory_propagation_justification( - get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), inter)); - ctx.assign(inter, js); - if (m.has_trace_stream()) { - log_axiom_instantiation(e); - m.trace_stream() << "[end-of-instance]\n"; - } - return; - } - eautomaton* a = get_automaton(e3); - if (!a) return; + IF_VERBOSE(11, verbose_stream() << mk_pp(s, m) << " in " << re << "\n"); + eautomaton* a = get_automaton(re); + if (!a) { + std::stringstream strm; + strm << "expression " << re << " does not correspond to a supported regular expression"; + TRACE("seq", tout << strm.str() << "\n";); + throw default_exception(strm.str()); + } - m_s_in_re.push_back(s_in_re(lit, s, e3, a)); + m_s_in_re.push_back(s_in_re(lit, s, re, a)); m_trail_stack.push(push_back_vector>(m_s_in_re)); expr_ref len = mk_len(s); @@ -4618,7 +4638,7 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { ptr_vector exprs; for (unsigned st : states) { - literal acc = mk_accept(s, zero, e3, st); + literal acc = mk_accept(s, zero, re, st); lits.push_back(acc); exprs.push_back(ctx.bool_var2expr(acc.var())); } @@ -4667,52 +4687,26 @@ static T* get_th_arith(context& ctx, theory_id afid, expr* e) { } } -static bool get_arith_value(context& ctx, theory_id afid, expr* e, expr_ref& v) { - theory_mi_arith* tha = get_th_arith(ctx, afid, e); - if (tha) return tha->get_value(ctx.get_enode(e), v); - theory_i_arith* thi = get_th_arith(ctx, afid, e); - if (thi) return thi->get_value(ctx.get_enode(e), v); - theory_lra* thr = get_th_arith(ctx, afid, e); - if (thr) return thr->get_value(ctx.get_enode(e), v); - TRACE("seq", tout << "no arithmetic theory\n";); - return false; -} 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()) { - return true; - } - next = next->get_next(); - } - while (next != n); - TRACE("seq", tout << "no value for " << mk_pp(e, m) << "\n";); - return false; + return m_arith_value.get_value_equiv(e, val) && val.is_int(); } -bool theory_seq::lower_bound(expr* _e, rational& lo) const { - context& ctx = get_context(); - expr_ref e = mk_len(_e); - expr_ref _lo(m); - family_id afid = m_autil.get_family_id(); - do { - theory_mi_arith* tha = get_th_arith(ctx, afid, e); - if (tha && tha->get_lower(ctx.get_enode(e), _lo)) break; - theory_i_arith* thi = get_th_arith(ctx, afid, e); - if (thi && thi->get_lower(ctx.get_enode(e), _lo)) break; - theory_lra* thr = get_th_arith(ctx, afid, e); - if (thr && thr->get_lower(ctx.get_enode(e), _lo)) break; - return false; - } - while (false); - return m_autil.is_numeral(_lo, lo) && lo.is_int(); +bool theory_seq::lower_bound(expr* e, rational& lo) const { + VERIFY(m_autil.is_int(e)); + bool is_strict = true; + return m_arith_value.get_lo(e, lo, is_strict) && !is_strict && lo.is_int(); + } +bool theory_seq::upper_bound(expr* e, rational& hi) const { + VERIFY(m_autil.is_int(e)); + bool is_strict = true; + return m_arith_value.get_up(e, hi, is_strict) && !is_strict && hi.is_int(); +} + + + // The difference with lower_bound function is that since in some cases, // the lower bound is not updated for all the enodes in the same eqc, // we have to traverse the eqc to query for the better lower bound. @@ -4752,26 +4746,8 @@ bool theory_seq::lower_bound2(expr* _e, rational& lo) { return true; } -bool theory_seq::upper_bound(expr* _e, rational& hi) const { - context& ctx = get_context(); - expr_ref e = mk_len(_e); - family_id afid = m_autil.get_family_id(); - expr_ref _hi(m); - do { - theory_mi_arith* tha = get_th_arith(ctx, afid, e); - if (tha && tha->get_upper(ctx.get_enode(e), _hi)) break; - theory_i_arith* thi = get_th_arith(ctx, afid, e); - if (thi && thi->get_upper(ctx.get_enode(e), _hi)) break; - theory_lra* thr = get_th_arith(ctx, afid, e); - if (thr && thr->get_upper(ctx.get_enode(e), _hi)) break; - return false; - } - while (false); - return m_autil.is_numeral(_hi, hi) && hi.is_int(); -} bool theory_seq::get_length(expr* e, rational& val) const { - context& ctx = get_context(); rational val1; expr_ref len(m), len_val(m); expr* e1 = nullptr, *e2 = nullptr; @@ -4801,9 +4777,7 @@ bool theory_seq::get_length(expr* e, rational& val) const { } else { len = mk_len(c); - if (ctx.e_internalized(len) && - get_arith_value(ctx, m_autil.get_family_id(), len, len_val) && - m_autil.is_numeral(len_val, val1)) { + if (m_arith_value.get_value(len, val1)) { val += val1; } else { @@ -4817,7 +4791,6 @@ bool theory_seq::get_length(expr* e, rational& val) const { } /* - TBD: check semantics of extract. let e = extract(s, i, l) @@ -4864,6 +4837,7 @@ void theory_seq::add_extract_axiom(expr* e) { add_extract_suffix_axiom(e, s, i); return; } + expr_ref x(mk_skolem(m_pre, s, i), m); expr_ref ls = mk_len(s); expr_ref lx = mk_len(x); @@ -4879,25 +4853,30 @@ void theory_seq::add_extract_axiom(expr* e) { literal li_ge_ls = mk_simplified_literal(m_autil.mk_ge(ls_minus_i_l, zero)); literal l_ge_zero = mk_simplified_literal(m_autil.mk_ge(l, zero)); literal ls_le_0 = mk_simplified_literal(m_autil.mk_le(ls, zero)); + literal le_is_0 = mk_eq(le, zero, false); add_axiom(~i_ge_0, ~ls_le_i, mk_seq_eq(xey, s)); add_axiom(~i_ge_0, ~ls_le_i, mk_eq(lx, i, false)); add_axiom(~i_ge_0, ~ls_le_i, ~l_ge_zero, ~li_ge_ls, mk_eq(le, l, false)); add_axiom(~i_ge_0, ~ls_le_i, li_ge_ls, mk_eq(le, mk_sub(ls, i), false)); add_axiom(~i_ge_0, ~ls_le_i, l_ge_zero, mk_eq(le, zero, false)); - add_axiom(i_ge_0, mk_eq(le, zero, false)); - add_axiom(ls_le_i, mk_eq(le, zero, false)); - add_axiom(~ls_le_0, mk_eq(le, zero, false)); + add_axiom(i_ge_0, le_is_0); + add_axiom(ls_le_i, le_is_0); + add_axiom(~ls_le_0, le_is_0); } void theory_seq::add_tail_axiom(expr* e, expr* s) { expr_ref head(m), tail(m); mk_decompose(s, head, tail); - add_axiom(mk_eq_empty(s), mk_seq_eq(s, mk_concat(head, e))); + literal emp = mk_eq_empty(s); + add_axiom(emp, mk_seq_eq(s, mk_concat(head, e))); + add_axiom(~emp, mk_eq_empty(e)); } void theory_seq::add_drop_last_axiom(expr* e, expr* s) { - add_axiom(mk_eq_empty(s), mk_seq_eq(s, mk_concat(e, m_util.str.mk_unit(mk_last(s))))); + literal emp = mk_eq_empty(s); + add_axiom(emp, mk_seq_eq(s, mk_concat(e, m_util.str.mk_unit(mk_last(s))))); + add_axiom(~emp, mk_eq_empty(e)); } bool theory_seq::is_drop_last(expr* s, expr* i, expr* l) { @@ -4938,6 +4917,7 @@ bool theory_seq::is_extract_suffix(expr* s, expr* i, expr* l) { /* 0 <= l <= len(s) => s = ey & l = len(e) len(s) < l => s = e + l < 0 => e = empty */ void theory_seq::add_extract_prefix_axiom(expr* e, expr* s, expr* l) { TRACE("seq", tout << mk_pp(e, m) << " " << mk_pp(s, m) << " " << mk_pp(l, m) << "\n";); @@ -4953,12 +4933,13 @@ void theory_seq::add_extract_prefix_axiom(expr* e, expr* s, expr* l) { add_axiom(~l_ge_0, ~l_le_s, mk_eq(l, le, false)); add_axiom(~l_ge_0, ~l_le_s, mk_eq(ls_minus_l, mk_len(y), false)); add_axiom(l_le_s, mk_eq(e, s, false)); + add_axiom(l_ge_0, mk_eq_empty(e)); } /* 0 <= i <= len(s) => s = xe & i = len(x) - i < 0 => len(e) = 0 - i > len(s) => len(e) = 0 + i < 0 => e = empty + i > len(s) => e = empty */ void theory_seq::add_extract_suffix_axiom(expr* e, expr* s, expr* i) { expr_ref x(mk_skolem(m_pre, s, i), m); @@ -4966,7 +4947,7 @@ void theory_seq::add_extract_suffix_axiom(expr* e, expr* s, expr* i) { expr_ref ls = mk_len(s); expr_ref zero(m_autil.mk_int(0), m); expr_ref xe = mk_concat(x, e); - literal le_is_0 = mk_eq(zero, mk_len(e), false); + literal le_is_0 = mk_eq_empty(e); literal i_ge_0 = mk_simplified_literal(m_autil.mk_ge(i, zero)); literal i_le_s = mk_simplified_literal(m_autil.mk_le(mk_sub(i, ls), zero)); add_axiom(~i_ge_0, ~i_le_s, mk_seq_eq(s, xe)); @@ -4984,36 +4965,61 @@ void theory_seq::add_extract_suffix_axiom(expr* e, expr* s, expr* i) { */ void theory_seq::add_at_axiom(expr* e) { + TRACE("seq", tout << "at-axiom: " << mk_pp(e, m) << "\n";); expr* s = nullptr, *i = nullptr; VERIFY(m_util.str.is_at(e, s, i)); - expr_ref len_e = mk_len(e); - expr_ref len_s = mk_len(s); expr_ref zero(m_autil.mk_int(0), m); expr_ref one(m_autil.mk_int(1), m); - expr_ref x = mk_skolem(m_pre, s, i); - //expr_ref y = mk_skolem(m_post, s, mk_sub(mk_sub(len_s, i), one)); - expr_ref y = mk_skolem(m_tail, s, i); - expr_ref xey = mk_concat(x, e, y); - expr_ref len_x = mk_len(x); expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); - + expr_ref len_s = mk_len(s); literal i_ge_0 = mk_simplified_literal(m_autil.mk_ge(i, zero)); literal i_ge_len_s = mk_simplified_literal(m_autil.mk_ge(mk_sub(i, mk_len(s)), zero)); - - add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(s, xey)); - add_axiom(~i_ge_0, i_ge_len_s, mk_eq(one, len_e, false)); - add_axiom(~i_ge_0, i_ge_len_s, mk_eq(i, len_x, false)); + rational iv; + if (m_autil.is_numeral(i, iv) && iv.is_int() && !iv.is_neg()) { + expr_ref_vector es(m); + expr_ref nth(m); + unsigned k = iv.get_unsigned(); + for (unsigned j = 0; j <= k; ++j) { + es.push_back(m_util.str.mk_unit(mk_nth(s, m_autil.mk_int(j)))); + } + nth = es.back(); + es.push_back(mk_skolem(m_tail, s, i)); + add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(s, m_util.str.mk_concat(es))); + add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(nth, e)); + } + else { + expr_ref len_e = mk_len(e); + expr_ref x = mk_skolem(m_pre, s, i); + expr_ref y = mk_skolem(m_tail, s, i); + expr_ref xey = mk_concat(x, e, y); + expr_ref len_x = mk_len(x); + add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(s, xey)); + add_axiom(~i_ge_0, i_ge_len_s, mk_eq(one, len_e, false)); + add_axiom(~i_ge_0, i_ge_len_s, mk_eq(i, len_x, false)); + } add_axiom(i_ge_0, mk_eq(e, emp, false)); add_axiom(~i_ge_len_s, mk_eq(e, emp, false)); } +void theory_seq::add_nth_axiom(expr* e) { + expr* s = nullptr, *i = nullptr; + rational n; + zstring str; + VERIFY(m_util.str.is_nth(e, s, i)); + if (m_util.str.is_string(s, str) && m_autil.is_numeral(i, n) && n.is_unsigned() && n.get_unsigned() < str.length()) { + app_ref ch(m_util.str.mk_char(str[n.get_unsigned()]), m); + add_axiom(mk_eq(ch, e, false)); + } +} + /* lit => s = (nth s 0) ++ (nth s 1) ++ ... ++ (nth s idx) ++ (tail s idx) */ void theory_seq::ensure_nth(literal lit, expr* s, expr* idx) { + TRACE("seq", tout << "ensure-nth: " << lit << " " << mk_pp(s, m) << " " << mk_pp(idx, m) << "\n";); rational r; SASSERT(get_context().get_assignment(lit) == l_true); VERIFY(m_autil.is_numeral(idx, r) && r.is_unsigned()); @@ -5112,13 +5118,15 @@ void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4, liter if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } -expr* theory_seq::coalesce_chars(expr* const& e) { + +expr_ref theory_seq::coalesce_chars(expr* const& e) { context& ctx = get_context(); expr* s; + unsigned ch; + expr_ref result(m); if (m_util.str.is_concat(e)) { - expr_ref_vector concats(m); + expr_ref_vector rs(m), concats(m); m_util.str.get_concat(e, concats); - expr_ref_vector result(m); for (unsigned i = 0; i < concats.size(); ++i) { expr_ref tmp(coalesce_chars(concats[i].get()), m); if (m_util.str.is_empty(tmp)) continue; @@ -5138,32 +5146,30 @@ expr* theory_seq::coalesce_chars(expr* const& e) { } } if (flag) { - result.push_back(m_util.str.mk_string(zs)); + rs.push_back(m_util.str.mk_string(zs)); if (i < concats.size()) - result.push_back(tmp); + rs.push_back(tmp); } else - result.push_back(tmp); + rs.push_back(tmp); } - SASSERT(result.size() > 0); - if (result.size() > 1) - return m_util.str.mk_concat(result.size(), result.c_ptr()); - else - return e; - } - else if (m_util.str.is_unit(e, s)) { - bv_util bvu(m); - if (bvu.is_bv(s)) { - expr_ref result(m); - expr * args[1] = {s}; - if (BR_FAILED != m_seq_rewrite.mk_app_core(to_app(e)->get_decl(), 1, args, result)) { - if (!ctx.e_internalized(result)) - ctx.internalize(result, false); - return result; - } + SASSERT(rs.size() > 0); + if (rs.size() > 1) { + return expr_ref(m_util.str.mk_concat(rs.size(), rs.c_ptr()), m); + } + else { + result = e; + return result; } } - return e; + else if (m_util.str.is_unit(e, s) && m_util.is_const_char(s, ch) && + BR_FAILED != m_seq_rewrite.mk_app_core(to_app(e)->get_decl(), 1, &s, result)) { + if (!ctx.e_internalized(result)) + ctx.internalize(result, false); + return result; + } + result = e; + return result; } expr_ref theory_seq::mk_skolem(symbol const& name, expr* e1, expr* e2, expr* e3, expr*e4, sort* range) { @@ -5173,9 +5179,11 @@ expr_ref theory_seq::mk_skolem(symbol const& name, expr* e1, expr* e2, expr* e3, if (!range) { range = m.get_sort(e1); } + expr_ref_vector pinned(m); if (name == m_seq_align) { for (unsigned i = 0; i < len; ++i) { - es[i] = coalesce_chars(es[i]); + pinned.push_back(coalesce_chars(es[i])); + es[i] = pinned.back(); TRACE("seq", tout << mk_pp(es[i], m) << "\n";); } } @@ -5250,6 +5258,8 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { f = mk_skolem(m_prefix, e1, e2); f = mk_concat(e1, f); propagate_eq(lit, f, e2, true); + //literal len1_le_len2 = mk_simplified_literal(m_autil.mk_ge(mk_sub(mk_len(e2), mk_len(e1)), m_autil.mk_int(0))); + //add_axiom(~lit, len1_le_len2); } else { propagate_not_prefix(e); @@ -5260,6 +5270,8 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { f = mk_skolem(m_suffix, e1, e2); f = mk_concat(f, e1); propagate_eq(lit, f, e2, true); + //literal len1_le_len2 = mk_simplified_literal(m_autil.mk_ge(mk_sub(mk_len(e2), mk_len(e1)), m_autil.mk_int(0))); + //add_axiom(~lit, len1_le_len2); } else { propagate_not_suffix(e); @@ -5288,6 +5300,8 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { expr_ref f2 = mk_skolem(m_indexof_right, e1, e2); f = mk_concat(f1, e2, f2); propagate_eq(lit, f, e1, true); + //literal len2_le_len1 = mk_simplified_literal(m_autil.mk_ge(mk_sub(mk_len(e1), mk_len(e2)), m_autil.mk_int(0))); + //add_axiom(~lit, len2_le_len1); } else if (!canonizes(false, e)) { propagate_non_empty(lit, e2); @@ -5338,6 +5352,26 @@ void theory_seq::new_eq_eh(theory_var v1, theory_var v2) { new_eq_eh(deps, n1, n2); } +lbool theory_seq::regex_are_equal(expr* r1, expr* r2) { + if (r1 == r2) { + return l_true; + } + expr* d1 = m_util.re.mk_inter(r1, m_util.re.mk_complement(r2)); + expr* d2 = m_util.re.mk_inter(r2, m_util.re.mk_complement(r1)); + expr_ref diff(m_util.re.mk_union(d1, d2), m); + eautomaton* aut = get_automaton(diff); + if (!aut) { + return l_undef; + } + else if (aut->is_empty()) { + return l_true; + } + else { + return l_false; + } +} + + void theory_seq::new_eq_eh(dependency* deps, enode* n1, enode* n2) { TRACE("seq", tout << expr_ref(n1->get_owner(), m) << " = " << expr_ref(n2->get_owner(), m) << "\n";); if (n1 != n2 && m_util.is_seq(n1->get_owner())) { @@ -5358,28 +5392,23 @@ void theory_seq::new_eq_eh(dependency* deps, enode* n1, enode* n2) { // create an expression for the symmetric difference and imply it is empty. enode_pair_vector eqs; literal_vector lits; - if (!linearize(deps, eqs, lits)) - return; context& ctx = get_context(); - eqs.push_back(enode_pair(n1, n2)); - expr_ref r1(n1->get_owner(), m); - expr_ref r2(n2->get_owner(), m); - ctx.get_rewriter()(r1); - ctx.get_rewriter()(r2); - if (r1 == r2) { - return; + switch (regex_are_equal(n1->get_owner(), n2->get_owner())) { + case l_true: + break; + case l_false: + if (!linearize(deps, eqs, lits)) { + throw default_exception("could not linearlize assumptions"); + } + eqs.push_back(enode_pair(n1, n2)); + ctx.set_conflict( + ctx.mk_justification( + ext_theory_conflict_justification( + get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), 0, nullptr))); + break; + default: + throw default_exception("convert regular expressions into automata"); } -#if 0 - expr* d1 = m_util.re.mk_inter(r1, m_util.re.mk_complement(r2)); - expr* d2 = m_util.re.mk_inter(r2, m_util.re.mk_complement(r1)); - expr_ref diff(m_util.re.mk_union(d1, d2), m); - lit = mk_literal(m_util.re.mk_is_empty(diff)); - justification* js = - ctx.mk_justification( - ext_theory_propagation_justification( - get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), lit)); - ctx.assign(lit, js); -#endif } } @@ -5389,6 +5418,26 @@ void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { expr_ref e1(n1->get_owner(), m); expr_ref e2(n2->get_owner(), m); SASSERT(n1->get_root() != n2->get_root()); + if (m_util.is_re(n1->get_owner())) { + enode_pair_vector eqs; + literal_vector lits; + context& ctx = get_context(); + switch (regex_are_equal(e1, e2)) { + case l_false: + return; + case l_true: { + literal lit = mk_eq(e1, e2, false); + lits.push_back(~lit); + ctx.set_conflict( + ctx.mk_justification( + ext_theory_conflict_justification( + get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), 0, nullptr))); + return; + } + default: + throw default_exception("convert regular expressions into automata"); + } + } m_exclude.update(e1, e2); expr_ref eq(m.mk_eq(e1, e2), m); TRACE("seq", tout << "new disequality " << get_context().get_scope_level() << ": " << eq << "\n";); @@ -5446,6 +5495,7 @@ void theory_seq::relevant_eh(app* n) { m_util.str.is_replace(n) || m_util.str.is_extract(n) || m_util.str.is_at(n) || + m_util.str.is_nth(n) || m_util.str.is_empty(n) || m_util.str.is_string(n) || m_util.str.is_itos(n) || @@ -5545,14 +5595,15 @@ void theory_seq::propagate_step(literal lit, expr* step) { TRACE("seq", tout << mk_pp(step, m) << " -> " << mk_pp(t, m) << "\n";); propagate_lit(nullptr, 1, &lit, mk_literal(t)); + expr_ref len_s = mk_len(s); rational lo; rational _idx; VERIFY(m_autil.is_numeral(idx, _idx)); - if (lower_bound(s, lo) && lo.is_unsigned() && lo >= _idx) { + if (lower_bound(len_s, lo) && lo.is_unsigned() && lo >= _idx) { // skip } else { - propagate_lit(nullptr, 1, &lit, ~mk_literal(m_autil.mk_le(mk_len(s), idx))); + propagate_lit(nullptr, 1, &lit, ~mk_literal(m_autil.mk_le(len_s, idx))); } ensure_nth(lit, s, idx); @@ -5568,24 +5619,22 @@ void theory_seq::propagate_step(literal lit, expr* step) { acc(s, idx, re, i) -> idx < max_unfolding */ void theory_seq::propagate_accept(literal lit, expr* acc) { + ++m_stats.m_propagate_automata; expr *e = nullptr, *idx = nullptr, *re = nullptr; unsigned src = 0; + context& ctx = get_context(); rational _idx; eautomaton* aut = nullptr; VERIFY(is_accept(acc, e, idx, re, src, aut)); + VERIFY(m_autil.is_numeral(idx, _idx)); VERIFY(aut); if (aut->is_sink_state(src)) { propagate_lit(nullptr, 1, &lit, false_literal); return; } - VERIFY(m_autil.is_numeral(idx, _idx)); - if (_idx.get_unsigned() > m_max_unfolding_depth && m_max_unfolding_lit != null_literal) { - propagate_lit(nullptr, 1, &lit, ~m_max_unfolding_lit); - return; - } + expr_ref len = mk_len(e); - literal_vector lits; lits.push_back(~lit); if (aut->is_final_state(src)) { @@ -5595,9 +5644,11 @@ void theory_seq::propagate_accept(literal lit, expr* acc) { else { propagate_lit(nullptr, 1, &lit, ~mk_literal(m_autil.mk_le(len, idx))); } + + eautomaton::moves mvs; aut->get_moves_from(src, mvs); - TRACE("seq", tout << mvs.size() << "\n";); + TRACE("seq", tout << mk_pp(acc, m) << " #moves " << mvs.size() << "\n";); ptr_vector exprs; for (auto const& mv : mvs) { expr_ref nth = mk_nth(e, idx); @@ -5609,16 +5660,22 @@ void theory_seq::propagate_accept(literal lit, expr* acc) { exprs.push_back(step_e); } if (m.has_trace_stream()) log_axiom_instantiation(get_manager().mk_implies(acc, get_manager().mk_or(exprs.size(), exprs.c_ptr()))); - get_context().mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; + + if (_idx.get_unsigned() > m_max_unfolding_depth && + m_max_unfolding_lit != null_literal && ctx.get_scope_level() > 0) { + propagate_lit(nullptr, 1, &lit, ~m_max_unfolding_lit); + } } void theory_seq::add_theory_assumptions(expr_ref_vector & assumptions) { TRACE("seq", tout << "add_theory_assumption " << m_util.has_re() << "\n";); if (m_util.has_re()) { - expr_ref dlimit = mk_max_unfolding_depth(); - m_max_unfolding_lit = mk_literal(dlimit); - TRACE("seq", tout << "add_theory_assumption " << dlimit << " " << assumptions << "\n";); + expr_ref dlimit(m); + dlimit = mk_max_unfolding_depth(); + m_trail_stack.push(value_trail(m_max_unfolding_lit)); + m_max_unfolding_lit = mk_literal(dlimit); assumptions.push_back(dlimit); } } @@ -5719,15 +5776,16 @@ bool theory_seq::canonizes(bool sign, expr* e) { } -void theory_seq::get_concat(expr* e, ptr_vector& concats) { +void theory_seq::get_ite_concat(expr* e, ptr_vector& concats) { expr* e1 = nullptr, *e2 = nullptr; while (true) { e = m_rep.find(e); + e = get_ite_value(e); if (m_util.str.is_concat(e, e1, e2)) { - get_concat(e1, concats); + get_ite_concat(e1, concats); e = e2; continue; - } + } concats.push_back(e); return; } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 83da23401..5520d4a84 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -19,9 +19,7 @@ Revision History: #ifndef THEORY_SEQ_H_ #define THEORY_SEQ_H_ -#include "smt/smt_theory.h" #include "ast/seq_decl_plugin.h" -#include "smt/theory_seq_empty.h" #include "ast/rewriter/th_rewriter.h" #include "ast/ast_trail.h" #include "util/scoped_vector.h" @@ -30,6 +28,9 @@ Revision History: #include "ast/rewriter/seq_rewriter.h" #include "util/union_find.h" #include "util/obj_ref_hashtable.h" +#include "smt/smt_theory.h" +#include "smt/smt_arith_value.h" +#include "smt/theory_seq_empty.h" namespace smt { @@ -343,14 +344,16 @@ namespace smt { unsigned m_axioms_head; // index of first axiom to add. bool m_incomplete; // is the solver (clearly) incomplete for the fragment. expr_ref_vector m_int_string; - obj_hashtable m_si_axioms; - obj_hashtable m_length; // is length applied + obj_map m_si_axioms; + obj_hashtable m_has_length; // is length applied + expr_ref_vector m_length; // length applications themselves scoped_ptr_vector m_replay; // set of actions to replay model_generator* m_mg; th_rewriter m_rewrite; seq_rewriter m_seq_rewrite; seq_util m_util; arith_util m_autil; + arith_value m_arith_value; th_trail_stack m_trail_stack; stats m_stats; symbol m_prefix, m_suffix, m_accept, m_reject; @@ -401,6 +404,8 @@ namespace smt { void init_search_eh() override; void init_model(expr_ref_vector const& es); + app* get_ite_value(expr* a); + void get_ite_concat(expr* e, ptr_vector& concats); void len_offset(expr* e, rational val); void prop_arith_to_len_offset(); @@ -415,12 +420,13 @@ namespace smt { bool reduce_length_eq(); bool branch_unit_variable(); // branch on XYZ = abcdef bool branch_binary_variable(); // branch on abcX = Ydefg + bool branch_variable(); // branch on bool branch_ternary_variable1(); // branch on XabcY = Zdefg or XabcY = defgZ bool branch_ternary_variable2(); // branch on XabcY = defgZmnpq bool branch_quat_variable(); // branch on XabcY = ZdefgT bool len_based_split(); // split based on len offset bool branch_variable_mb(); // branch on a variable, model based on length - bool branch_variable(); // branch on a variable + bool branch_variable_eq(); // branch on a variable, by an alignment among variable boundaries. bool is_solved(); bool check_length_coherence(); bool check_length_coherence0(expr* e); @@ -428,7 +434,7 @@ namespace smt { bool fixed_length(bool is_zero = false); bool fixed_length(expr* e, bool is_zero); void branch_unit_variable(dependency* dep, expr* X, expr_ref_vector const& units); - bool branch_variable(eq const& e); + bool branch_variable_eq(eq const& e); bool branch_binary_variable(eq const& e); bool eq_unit(expr* const& l, expr* const &r) const; unsigned_vector overlap(expr_ref_vector const& ls, expr_ref_vector const& rs); @@ -446,6 +452,7 @@ namespace smt { vector const& ll, vector const& rl); bool set_empty(expr* x); bool is_complex(eq const& e); + lbool regex_are_equal(expr* r1, expr* r2); bool check_extensionality(); bool check_contains(); @@ -536,8 +543,6 @@ namespace smt { expr_ref expand1(expr* e, dependency*& eqs); expr_ref try_expand(expr* e, dependency*& eqs); void add_dependency(dependency*& dep, enode* a, enode* b); - - void get_concat(expr* e, ptr_vector& concats); // terms whose meaning are encoded using axioms. void enque_axiom(expr* e); @@ -558,7 +563,7 @@ namespace smt { bool is_extract_suffix(expr* s, expr* i, expr* l); - bool has_length(expr *e) const { return m_length.contains(e); } + bool has_length(expr *e) const { return m_has_length.contains(e); } void add_length(expr* e); void enforce_length(expr* n); bool enforce_length(expr_ref_vector const& es, vector& len); @@ -571,6 +576,7 @@ namespace smt { expr_ref add_elim_string_axiom(expr* n); void add_at_axiom(expr* n); + void add_nth_axiom(expr* n); void add_in_re_axiom(expr* n); void add_itos_axiom(expr* n); void add_stoi_axiom(expr* n); @@ -604,7 +610,7 @@ namespace smt { bool get_length(expr* s, rational& val) const; void mk_decompose(expr* e, expr_ref& head, expr_ref& tail); - expr* coalesce_chars(expr* const& str); + expr_ref coalesce_chars(expr* const& str); expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = nullptr, expr* e3 = nullptr, expr* e4 = nullptr, sort* range = nullptr); bool is_skolem(symbol const& s, expr* e) const; diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 3bfda2379..50e666c70 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -507,7 +507,7 @@ namespace smt { app * a = mk_fresh_const(name.c_str(), int_sort); ctx.internalize(a, false); - SASSERT(ctx.get_enode(a) != NULL); + SASSERT(ctx.get_enode(a) != nullptr); SASSERT(ctx.e_internalized(a)); ctx.mark_as_relevant(a); // I'm assuming that this combination will do the correct thing in the integer theory. @@ -546,7 +546,7 @@ namespace smt { // I have a hunch that this may not get internalized for free... ctx.internalize(a, false); - SASSERT(ctx.get_enode(a) != NULL); + SASSERT(ctx.get_enode(a) != nullptr); SASSERT(ctx.e_internalized(a)); // this might help?? mk_var(ctx.get_enode(a)); @@ -568,7 +568,7 @@ namespace smt { m_trail.push_back(a); ctx.internalize(a, false); - SASSERT(ctx.get_enode(a) != NULL); + SASSERT(ctx.get_enode(a) != nullptr); SASSERT(ctx.e_internalized(a)); mk_var(ctx.get_enode(a)); m_basicstr_axiom_todo.push_back(ctx.get_enode(a)); @@ -619,7 +619,7 @@ namespace smt { app * a = mk_fresh_const(name.c_str(), string_sort); ctx.internalize(a, false); - SASSERT(ctx.get_enode(a) != NULL); + SASSERT(ctx.get_enode(a) != nullptr); // this might help?? mk_var(ctx.get_enode(a)); @@ -712,7 +712,7 @@ namespace smt { * Returns the simplified concatenation of two expressions, * where either both expressions are constant strings * or one expression is the empty string. - * If this precondition does not hold, the function returns NULL. + * If this precondition does not hold, the function returns nullptr. * (note: this function was strTheory::Concat()) */ expr * theory_str::mk_concat_const_str(expr * n1, expr * n2) { @@ -1667,53 +1667,66 @@ namespace smt { } } + // (str.replace s t t') is the string obtained by replacing the first occurrence + // of t in s, if any, by t'. Note that if t is empty, the result is to prepend + // t' to s; also, if t does not occur in s then the result is s. void theory_str::instantiate_axiom_Replace(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 Replace axiom for " << mk_pp(expr, m) << std::endl;); + app * ex = e->get_owner(); + if (axiomatized_terms.contains(ex)) { + TRACE("str", tout << "already set up Replace axiom for " << mk_pp(ex, m) << std::endl;); return; } - axiomatized_terms.insert(expr); + axiomatized_terms.insert(ex); - TRACE("str", tout << "instantiate Replace axiom for " << mk_pp(expr, m) << std::endl;); + TRACE("str", tout << "instantiate Replace axiom for " << mk_pp(ex, m) << std::endl;); expr_ref x1(mk_str_var("x1"), m); expr_ref x2(mk_str_var("x2"), m); expr_ref i1(mk_int_var("i1"), m); expr_ref result(mk_str_var("result"), m); + expr * replaceS = nullptr; + expr * replaceT = nullptr; + expr * replaceTPrime = nullptr; + VERIFY(u.str.is_replace(ex, replaceS, replaceT, replaceTPrime)); + + // t empty => result = (str.++ t' s) + expr_ref emptySrcAst(ctx.mk_eq_atom(replaceT, mk_string("")), m); + expr_ref prependTPrimeToS(ctx.mk_eq_atom(result, mk_concat(replaceTPrime, replaceS)), m); + // condAst = Contains(args[0], args[1]) - expr_ref condAst(mk_contains(expr->get_arg(0), expr->get_arg(1)), m); + expr_ref condAst(mk_contains(ex->get_arg(0), ex->get_arg(1)), m); // ----------------------- // true branch expr_ref_vector thenItems(m); // args[0] = x1 . args[1] . x2 - thenItems.push_back(ctx.mk_eq_atom(expr->get_arg(0), mk_concat(x1, mk_concat(expr->get_arg(1), x2)))); + thenItems.push_back(ctx.mk_eq_atom(ex->get_arg(0), mk_concat(x1, mk_concat(ex->get_arg(1), x2)))); // i1 = |x1| thenItems.push_back(ctx.mk_eq_atom(i1, mk_strlen(x1))); // args[0] = x3 . x4 /\ |x3| = |x1| + |args[1]| - 1 /\ ! contains(x3, args[1]) expr_ref x3(mk_str_var("x3"), m); expr_ref x4(mk_str_var("x4"), m); - 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))); + expr_ref tmpLen(m_autil.mk_add(i1, mk_strlen(ex->get_arg(1)), mk_int(-1)), m); + thenItems.push_back(ctx.mk_eq_atom(ex->get_arg(0), mk_concat(x3, x4))); thenItems.push_back(ctx.mk_eq_atom(mk_strlen(x3), tmpLen)); - 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)))); + thenItems.push_back(mk_not(m, mk_contains(x3, ex->get_arg(1)))); + thenItems.push_back(ctx.mk_eq_atom(result, mk_concat(x1, mk_concat(ex->get_arg(2), x2)))); // ----------------------- // false branch - expr_ref elseBranch(ctx.mk_eq_atom(result, expr->get_arg(0)), m); + expr_ref elseBranch(ctx.mk_eq_atom(result, ex->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); + expr_ref breakdownAssert(m.mk_ite(emptySrcAst, prependTPrimeToS, + m.mk_ite(condAst, mk_and(thenItems), elseBranch)), m); expr_ref breakdownAssert_rw(breakdownAssert, m); rw(breakdownAssert_rw); assert_axiom(breakdownAssert_rw); - expr_ref reduceToResult(ctx.mk_eq_atom(expr, result), m); + expr_ref reduceToResult(ctx.mk_eq_atom(ex, result), m); expr_ref reduceToResult_rw(reduceToResult, m); rw(reduceToResult_rw); assert_axiom(reduceToResult_rw); @@ -2154,7 +2167,7 @@ namespace smt { // Evaluates the concatenation (n1 . n2) with respect to // the current equivalence classes of n1 and n2. // Returns a constant string expression representing this concatenation - // if one can be determined, or NULL if this is not possible. + // if one can be determined, or nullptr if this is not possible. expr * theory_str::eval_concat(expr * n1, expr * n2) { bool n1HasEqcValue = false; bool n2HasEqcValue = false; @@ -2228,7 +2241,7 @@ namespace smt { for (enode_vector::iterator parent_it = current_parents.begin(); parent_it != current_parents.end(); ++parent_it) { enode * e_parent = *parent_it; - SASSERT(e_parent != NULL); + SASSERT(e_parent != nullptr); app * a_parent = e_parent->get_owner(); TRACE("str", tout << "considering parent " << mk_ismt2_pp(a_parent, m) << std::endl;); @@ -2507,7 +2520,7 @@ namespace smt { } } - if (resolvedMap.size() == 0) { + if (resolvedMap.empty()) { // no simplification possible return node; } else { @@ -4844,6 +4857,7 @@ namespace smt { bool theory_str::get_arith_value(expr* e, rational& val) const { context& ctx = get_context(); ast_manager & m = get_manager(); + (void)m; if (!ctx.e_internalized(e)) { return false; } @@ -4867,9 +4881,10 @@ namespace smt { return false; } - arith_value v(get_context()); + arith_value v(get_manager()); + v.init(&get_context()); bool strict; - return v.get_lo(_e, lo, strict); + return v.get_lo_equiv(_e, lo, strict); } bool theory_str::upper_bound(expr* _e, rational& hi) { @@ -4878,9 +4893,10 @@ namespace smt { return false; } - arith_value v(get_context()); + arith_value v(get_manager()); + v.init(&get_context()); bool strict; - return v.get_up(_e, hi, strict); + return v.get_up_equiv(_e, hi, strict); } bool theory_str::get_len_value(expr* e, rational& val) { @@ -5458,7 +5474,7 @@ namespace smt { } if (implyR) { - if (litems1.size() == 0) { + if (litems1.empty()) { assert_axiom(implyR); } else { assert_implication(mk_and(litems1), implyR); @@ -5581,7 +5597,7 @@ namespace smt { tout << " " << mk_pp(el, m); } tout << std::endl; - if (constStrAst == NULL) { + if (constStrAst == nullptr) { tout << "constStrAst = NULL" << std::endl; } else { tout << "constStrAst = " << mk_pp(constStrAst, m) << std::endl; @@ -7026,7 +7042,7 @@ namespace smt { bool theory_str::refine_automaton_lower_bound(eautomaton * aut, rational current_lower_bound, rational & refined_lower_bound) { ENSURE(aut != nullptr); - if (aut->final_states().size() < 1) { + if (aut->final_states().empty()) { // no solutions at all refined_lower_bound = rational::minus_one(); return false; @@ -7210,20 +7226,18 @@ namespace smt { expr_ref theory_str::aut_path_rewrite_constraint(expr * cond, expr * ch_var) { context & ctx = get_context(); ast_manager & m = get_manager(); - bv_util bvu(m); expr_ref retval(m); - - rational char_val; - unsigned int bv_width; + + unsigned char_val = 0; expr * lhs; expr * rhs; - if (bvu.is_numeral(cond, char_val, bv_width)) { - SASSERT(char_val.is_nonneg() && char_val.get_unsigned() < 256); + if (u.is_const_char(cond, char_val)) { + SASSERT(char_val < 256); TRACE("str", tout << "rewrite character constant " << char_val << std::endl;); - zstring str_const(char_val.get_unsigned()); + zstring str_const(char_val); retval = u.str.mk_string(str_const); return retval; } else if (is_var(cond)) { @@ -7368,23 +7382,20 @@ namespace smt { } else if (mv.t()->is_range()) { expr_ref range_lo(mv.t()->get_lo(), m); expr_ref range_hi(mv.t()->get_hi(), m); - bv_util bvu(m); - rational lo_val, hi_val; - unsigned int bv_width; + unsigned lo_val, hi_val; - if (bvu.is_numeral(range_lo, lo_val, bv_width) && bvu.is_numeral(range_hi, hi_val, bv_width)) { + if (u.is_const_char(range_lo, lo_val) && u.is_const_char(range_hi, hi_val)) { TRACE("str", tout << "make range predicate from " << lo_val << " to " << hi_val << std::endl;); expr_ref cond_rhs(m); if (hi_val < lo_val) { - rational tmp = lo_val; - lo_val = hi_val; - hi_val = tmp; + // NSB: why? The range would be empty. + std::swap(lo_val, hi_val); } expr_ref_vector cond_rhs_terms(m); - for (unsigned i = lo_val.get_unsigned(); i <= hi_val.get_unsigned(); ++i) { + for (unsigned i = lo_val; i <= hi_val; ++i) { zstring str_const(i); expr_ref str_expr(u.str.mk_string(str_const), m); cond_rhs_terms.push_back(ctx.mk_eq_atom(ch, str_expr)); @@ -7492,15 +7503,12 @@ namespace smt { expr_ref newConcat(m); if (arg1 != a1 || arg2 != a2) { TRACE("str", tout << "resolved concat argument(s) to eqc string constants" << std::endl;); - int iPos = 0; expr_ref_vector item1(m); if (a1 != arg1) { item1.push_back(ctx.mk_eq_atom(a1, arg1)); - iPos += 1; } if (a2 != arg2) { item1.push_back(ctx.mk_eq_atom(a2, arg2)); - iPos += 1; } expr_ref implyL1(mk_and(item1), m); newConcat = mk_concat(arg1, arg2); @@ -7793,7 +7801,7 @@ namespace smt { generate_mutual_exclusion(arrangement_disjunction); } } /* (arg1Len != 1 || arg2Len != 1) */ - } /* if (Concat(arg1, arg2) == NULL) */ + } /* if (Concat(arg1, arg2) == nullptr) */ } } } @@ -8174,13 +8182,13 @@ namespace smt { // step 2: Concat == Constant - if (eqc_const_lhs.size() != 0) { + if (!eqc_const_lhs.empty()) { expr * conStr = *(eqc_const_lhs.begin()); std::set::iterator itor2 = eqc_concat_rhs.begin(); for (; itor2 != eqc_concat_rhs.end(); itor2++) { solve_concat_eq_str(*itor2, conStr); } - } else if (eqc_const_rhs.size() != 0) { + } else if (!eqc_const_rhs.empty()) { expr* conStr = *(eqc_const_rhs.begin()); std::set::iterator itor1 = eqc_concat_lhs.begin(); for (; itor1 != eqc_concat_lhs.end(); itor1++) { @@ -8254,9 +8262,10 @@ namespace smt { void theory_str::check_eqc_concat_concat(std::set & eqc_concat_lhs, std::set & eqc_concat_rhs) { ast_manager & m = get_manager(); + (void)m; int hasCommon = 0; - if (eqc_concat_lhs.size() != 0 && eqc_concat_rhs.size() != 0) { + if (!eqc_concat_lhs.empty() && !eqc_concat_rhs.empty()) { std::set::iterator itor1 = eqc_concat_lhs.begin(); std::set::iterator itor2 = eqc_concat_rhs.begin(); for (; itor1 != eqc_concat_lhs.end(); itor1++) { @@ -8576,13 +8585,13 @@ namespace smt { obj_map >::iterator varItor = cut_var_map.begin(); while (varItor != cut_var_map.end()) { std::stack & val = cut_var_map[varItor->m_key]; - while ((val.size() > 0) && (val.top()->level != 0) && (val.top()->level >= sLevel)) { + while ((!val.empty()) && (val.top()->level != 0) && (val.top()->level >= sLevel)) { // TRACE("str", tout << "remove cut info for " << mk_pp(e, get_manager()) << std::endl; print_cut_var(e, tout);); // T_cut * aCut = val.top(); val.pop(); // dealloc(aCut); } - if (val.size() == 0) { + if (val.empty()) { cutvarmap_removes.insert(varItor->m_key); } varItor++; @@ -9408,22 +9417,22 @@ namespace smt { } } - if (depMap.size() == 0) { + if (depMap.empty()) { std::map::iterator itor = strVarMap.begin(); for (; itor != strVarMap.end(); itor++) { expr * var = get_alias_index_ast(aliasIndexMap, itor->first); if (lrConstrainedMap.find(var) == lrConstrainedMap.end()) { freeVarMap[var] = 1; } else { - int lrConstainted = 0; + int lrConstrained = 0; std::map::iterator lrit = freeVarMap.begin(); for (; lrit != freeVarMap.end(); lrit++) { if (lrConstrainedMap[var].find(lrit->first) != lrConstrainedMap[var].end()) { - lrConstainted = 1; + lrConstrained = 1; break; } } - if (lrConstainted == 0) { + if (lrConstrained == 0) { freeVarMap[var] = 1; } } @@ -9442,15 +9451,15 @@ namespace smt { if (lrConstrainedMap.find(var) == lrConstrainedMap.end()) { freeVarMap[var] = 1; } else { - int lrConstainted = 0; + int lrConstrained = 0; std::map::iterator lrit = freeVarMap.begin(); for (; lrit != freeVarMap.end(); lrit++) { if (lrConstrainedMap[var].find(lrit->first) != lrConstrainedMap[var].end()) { - lrConstainted = 1; + lrConstrained = 1; break; } } - if (lrConstainted == 0) { + if (lrConstrained == 0) { freeVarMap[var] = 1; } } @@ -9462,15 +9471,15 @@ namespace smt { if (lrConstrainedMap.find(var) == lrConstrainedMap.end()) { freeVarMap[var] = 1; } else { - int lrConstainted = 0; + int lrConstrained = 0; std::map::iterator lrit = freeVarMap.begin(); for (; lrit != freeVarMap.end(); lrit++) { if (lrConstrainedMap[var].find(lrit->first) != lrConstrainedMap[var].end()) { - lrConstainted = 1; + lrConstrained = 1; break; } } - if (lrConstainted == 0) { + if (lrConstrained == 0) { freeVarMap[var] = 1; } } @@ -9491,15 +9500,15 @@ namespace smt { if (lrConstrainedMap.find(var) == lrConstrainedMap.end()) { freeVarMap[var] = 1; } else { - int lrConstainted = 0; + int lrConstrained = 0; std::map::iterator lrit = freeVarMap.begin(); for (; lrit != freeVarMap.end(); lrit++) { if (lrConstrainedMap[var].find(lrit->first) != lrConstrainedMap[var].end()) { - lrConstainted = 1; + lrConstrained = 1; break; } } - if (lrConstainted == 0) { + if (lrConstrained == 0) { freeVarMap[var] = 1; } } @@ -9753,7 +9762,7 @@ namespace smt { expr_ref concatlenExpr (mk_strlen(concat), m) ; bool allLeafResolved = true; if (! get_arith_value(concatlenExpr, lenValue)) { - // the length fo concat is unresolved yet + // the length of concat is unresolved yet if (get_len_value(concat, lenValue)) { // but all leaf nodes have length information TRACE("str", tout << "* length pop-up: " << mk_ismt2_pp(concat, m) << "| = " << lenValue << std::endl;); @@ -10423,12 +10432,12 @@ namespace smt { } } // foreach(term in str_in_re_terms) - eautomaton * aut_inter = NULL; + eautomaton * aut_inter = nullptr; CTRACE("str", !intersect_constraints.empty(), tout << "check intersection of automata constraints for " << mk_pp(str, m) << std::endl;); for (svector::iterator aut_it = intersect_constraints.begin(); aut_it != intersect_constraints.end(); ++aut_it) { regex_automaton_under_assumptions aut = *aut_it; - if (aut_inter == NULL) { + if (aut_inter == nullptr) { // start somewhere aut_inter = aut.get_automaton(); used_intersect_constraints.push_back(aut); @@ -10478,7 +10487,7 @@ namespace smt { } } } // foreach(entry in intersect_constraints) - if (aut_inter != NULL) { + if (aut_inter != nullptr) { aut_inter->compress(); } TRACE("str", tout << "intersected " << used_intersect_constraints.size() << " constraints" << std::endl;); @@ -10509,7 +10518,7 @@ namespace smt { } conflict_lhs = mk_and(conflict_terms); - if (used_intersect_constraints.size() > 1 && aut_inter != NULL) { + if (used_intersect_constraints.size() > 1 && aut_inter != nullptr) { // check whether the intersection is only the empty string unsigned initial_state = aut_inter->init(); if (aut_inter->final_states().size() == 1 && aut_inter->is_final_state(initial_state)) { @@ -10527,7 +10536,7 @@ namespace smt { } } - if (aut_inter != NULL && aut_inter->is_empty()) { + if (aut_inter != nullptr && aut_inter->is_empty()) { TRACE("str", tout << "product automaton is empty; asserting conflict clause" << std::endl;); expr_ref conflict_clause(m.mk_not(mk_and(conflict_terms)), m); assert_axiom(conflict_clause); @@ -10807,7 +10816,7 @@ namespace smt { expr * var = fvIt2->first; tmpSet.clear(); get_eqc_allUnroll(var, constValue, tmpSet); - if (tmpSet.size() > 0) { + if (!tmpSet.empty()) { fv_unrolls_map[var] = tmpSet; } } @@ -10941,7 +10950,7 @@ namespace smt { expr * var = fvIt1->first; fSimpUnroll.clear(); get_eqc_simpleUnroll(var, constValue, fSimpUnroll); - if (fSimpUnroll.size() == 0) { + if (fSimpUnroll.empty()) { gen_assign_unroll_reg(fv_unrolls_map[var]); } else { expr * toAssert = gen_assign_unroll_Str2Reg(var, fSimpUnroll); @@ -11554,7 +11563,7 @@ namespace smt { unroll_tries_map[var][unrolls].erase(e); } - if (unroll_tries_map[var][unrolls].size() == 0) { + if (unroll_tries_map[var][unrolls].empty()) { unroll_tries_map[var][unrolls].push_back(mk_unroll_test_var()); } @@ -11815,7 +11824,7 @@ namespace smt { expr_ref assertL(mk_and(and_items_LHS), m); SASSERT(assertL); expr * finalAxiom = m.mk_or(m.mk_not(assertL), lenTestAssert.get()); - SASSERT(finalAxiom != NULL); + SASSERT(finalAxiom != nullptr); TRACE("str", tout << "crash avoidance finalAxiom: " << mk_pp(finalAxiom, m) << std::endl;); return finalAxiom; } else { @@ -12101,7 +12110,7 @@ namespace smt { lenTester_fvar_map.insert(indicator, freeVar); expr * lenTestAssert = gen_len_test_options(freeVar, indicator, testNum); - SASSERT(lenTestAssert != NULL); + SASSERT(lenTestAssert != nullptr); return lenTestAssert; } else { TRACE("str", tout << "found previous in-scope length assertions" << std::endl;); @@ -12207,7 +12216,7 @@ namespace smt { testNum = i + 1; } expr * lenTestAssert = gen_len_test_options(freeVar, indicator, testNum); - SASSERT(lenTestAssert != NULL); + SASSERT(lenTestAssert != nullptr); return lenTestAssert; } else { // if we are performing automata-based reasoning and the term associated with diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index b051c504a..fba549412 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -618,7 +618,7 @@ namespace smt { th_var v1 = null_theory_var, v2 = null_theory_var; bool pos1 = true, pos2 = true; - if (terms.size() >= 1) { + if (!terms.empty()) { v1 = terms[0].first; pos1 = terms[0].second.is_one(); SASSERT(v1 != null_theory_var); diff --git a/src/smt/theory_wmaxsat.cpp b/src/smt/theory_wmaxsat.cpp index 75528a07e..53172b07e 100644 --- a/src/smt/theory_wmaxsat.cpp +++ b/src/smt/theory_wmaxsat.cpp @@ -103,8 +103,8 @@ namespace smt { m_normalize = true; bool_var bv = register_var(var, true); (void)bv; - TRACE("opt", tout << "enable: v" << m_bool2var[bv] << " b" << bv << " " << mk_pp(var, get_manager()) << "\n"; - tout << wfml << "\n";); + TRACE("opt", tout << "inc: " << ctx.inconsistent() << " enable: v" << m_bool2var[bv] + << " b" << bv << " " << mk_pp(var, get_manager()) << "\n" << wfml << "\n";); return var; } @@ -134,8 +134,10 @@ namespace smt { theory_var v = mk_var(x); ctx.attach_th_var(x, this, v); m_bool2var.insert(bv, v); - SASSERT(v == static_cast(m_var2bool.size())); - m_var2bool.push_back(bv); + while (m_var2bool.size() <= static_cast(v)) { + m_var2bool.push_back(null_bool_var); + } + m_var2bool[v] = bv; SASSERT(ctx.bool_var2enode(bv)); } return bv; diff --git a/src/smt/watch_list.cpp b/src/smt/watch_list.cpp index 778e93021..f95e1c571 100644 --- a/src/smt/watch_list.cpp +++ b/src/smt/watch_list.cpp @@ -21,7 +21,7 @@ Revision History: namespace smt { #define DEFAULT_WATCH_LIST_SIZE (sizeof(clause *) * 4) -#ifdef _AMD64_ +#if defined(__LP64__) || defined(_WIN64) // make sure data is aligned in 64 bit machines #define HEADER_SIZE (4 * sizeof(unsigned)) #else @@ -38,7 +38,7 @@ namespace smt { if (m_data == nullptr) { unsigned size = DEFAULT_WATCH_LIST_SIZE + HEADER_SIZE; unsigned * mem = reinterpret_cast(alloc_svect(char, size)); -#ifdef _AMD64_ +#if defined(__LP64__) || defined(_WIN64) ++mem; // make sure data is aligned in 64 bit machines #endif *mem = 0; @@ -61,7 +61,7 @@ namespace smt { unsigned new_capacity = (((curr_capacity * 3 + sizeof(clause *)) >> 1)+3)&~3U; unsigned * mem = reinterpret_cast(alloc_svect(char, new_capacity + HEADER_SIZE)); unsigned curr_end_cls = end_cls_core(); -#ifdef _AMD64_ +#if defined(__LP64__) || defined(_WIN64) ++mem; // make sure data is aligned in 64 bit machines #endif *mem = curr_end_cls; diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt index c8e206f7f..281a34018 100644 --- a/src/solver/CMakeLists.txt +++ b/src/solver/CMakeLists.txt @@ -16,5 +16,7 @@ z3_add_component(solver PYG_FILES combined_solver_params.pyg parallel_params.pyg + PYG_FILES + solver_params.pyg ) diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index e8fb34815..b939efc6b 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -218,7 +218,7 @@ public: return l_undef; } - lbool check_sat(unsigned num_assumptions, expr * const * assumptions) override { + lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { m_check_sat_executed = true; m_use_solver1_results = false; @@ -227,13 +227,13 @@ public: m_ignore_solver1) { // must use incremental solver switch_inc_mode(); - return m_solver2->check_sat(num_assumptions, assumptions); + return m_solver2->check_sat_core(num_assumptions, assumptions); } if (m_inc_mode) { if (m_inc_timeout == UINT_MAX) { IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"using solver 2 (without a timeout)\")\n";); - lbool r = m_solver2->check_sat(num_assumptions, assumptions); + lbool r = m_solver2->check_sat_core(num_assumptions, assumptions); if (r != l_undef || !use_solver1_when_undef() || get_manager().canceled()) { return r; } @@ -244,7 +244,7 @@ public: lbool r = l_undef; try { scoped_timer timer(m_inc_timeout, &eh); - r = m_solver2->check_sat(num_assumptions, assumptions); + r = m_solver2->check_sat_core(num_assumptions, assumptions); } catch (z3_exception&) { if (!eh.m_canceled) { @@ -260,7 +260,7 @@ public: IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"using solver 1\")\n";); m_use_solver1_results = true; - return m_solver1->check_sat(num_assumptions, assumptions); + return m_solver1->check_sat_core(num_assumptions, assumptions); } void set_progress_callback(progress_callback * callback) override { @@ -314,6 +314,25 @@ public: m_solver2->get_model(m); } + void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { + if (m_use_solver1_results) + m_solver1->get_levels(vars, depth); + else + m_solver2->get_levels(vars, depth); + } + + expr_ref_vector get_trail() override { + if (m_use_solver1_results) + return m_solver1->get_trail(); + else + return m_solver2->get_trail(); + } + + void set_activity(expr* lit, double activity) override { + m_solver1->set_activity(lit, activity); + m_solver2->set_activity(lit, activity); + } + proof * get_proof() override { if (m_use_solver1_results) return m_solver1->get_proof(); diff --git a/src/solver/parallel_tactic.cpp b/src/solver/parallel_tactic.cpp index 4ee93ff37..d820dbd9b 100644 --- a/src/solver/parallel_tactic.cpp +++ b/src/solver/parallel_tactic.cpp @@ -213,7 +213,7 @@ class parallel_tactic : public tactic { 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()); + ast_manager* new_m = alloc(ast_manager, m, true); ast_translation tr(m, *new_m); solver* s = m_solver.get()->translate(*new_m, m_params); solver_state* st = alloc(solver_state, new_m, s, m_params); @@ -330,6 +330,7 @@ private: volatile bool m_has_undef; bool m_allsat; unsigned m_num_unsat; + unsigned m_last_depth; int m_exn_code; std::string m_exn_msg; @@ -340,7 +341,8 @@ private: m_has_undef = false; m_allsat = false; m_branches = 0; - m_num_unsat = 0; + m_num_unsat = 0; + m_last_depth = 0; m_backtrack_frequency = pp.conquer_backtrack_frequency(); m_conquer_delay = pp.conquer_delay(); m_exn_code = 0; @@ -350,9 +352,10 @@ private: void log_branches(lbool status) { IF_VERBOSE(0, 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() << ":closed " << m_num_unsat << " :open " << m_branches << ")\n";); + if (status == l_true) verbose_stream() << ":status sat"; + if (status == l_undef) verbose_stream() << ":status unknown"; + if (m_num_unsat > 0) verbose_stream() << " :closed " << m_num_unsat << "@" << m_last_depth; + verbose_stream() << " :open " << m_branches << ")\n";); } void add_branches(unsigned b) { @@ -388,10 +391,15 @@ private: log_branches(status); } - void report_sat(solver_state& s) { + void report_sat(solver_state& s, solver* conquer) { close_branch(s, l_true); model_ref mdl; - s.get_solver().get_model(mdl); + if (conquer) { + conquer->get_model(mdl); + } + else { + s.get_solver().get_model(mdl); + } if (mdl) { std::lock_guard lock(m_mutex); if (&s.m() != &m_manager) { @@ -405,13 +413,14 @@ private: } } - void inc_unsat() { + void inc_unsat(solver_state& s) { std::lock_guard lock(m_mutex); ++m_num_unsat; + m_last_depth = s.get_depth(); } void report_unsat(solver_state& s) { - inc_unsat(); + inc_unsat(s); close_branch(s, l_false); if (s.has_assumptions()) { expr_ref_vector core(s.m()); @@ -449,7 +458,7 @@ private: if (canceled(s)) return; switch (s.simplify()) { case l_undef: break; - case l_true: report_sat(s); return; + case l_true: report_sat(s, nullptr); return; case l_false: report_unsat(s); return; } if (canceled(s)) return; @@ -489,12 +498,12 @@ private: IF_VERBOSE(0, verbose_stream() << "(tactic.parallel :backtrack " << cutoff << " -> " << c.size() << ")\n"); cutoff = c.size(); } - inc_unsat(); + inc_unsat(s); log_branches(l_false); break; case l_true: - report_sat(s); + report_sat(s, conquer.get()); if (conquer) { collect_statistics(*conquer.get()); } diff --git a/src/solver/smt_logics.cpp b/src/solver/smt_logics.cpp index d0fd8f809..a8dcf2f90 100644 --- a/src/solver/smt_logics.cpp +++ b/src/solver/smt_logics.cpp @@ -80,6 +80,7 @@ bool smt_logics::logic_has_arith(symbol const & s) { s == "LRA" || s == "UFIDL" || s == "QF_FP" || + s == "FP" || s == "QF_FPBV" || s == "QF_BVFP" || s == "QF_S" || @@ -101,6 +102,7 @@ bool smt_logics::logic_has_bv(symbol const & s) { s == "QF_AUFBV" || s == "QF_BVRE" || s == "QF_FPBV" || + s == "FP" || s == "QF_BVFP" || logic_is_allcsp(s) || s == "QF_FD" || @@ -138,7 +140,7 @@ bool smt_logics::logic_has_str(symbol const & s) { } bool smt_logics::logic_has_fpa(symbol const & s) { - return s == "QF_FP" || s == "QF_FPBV" || s == "QF_BVFP" || s == "QF_FPLRA" || logic_is_allcsp(s); + return s == "FP" || s == "QF_FP" || s == "QF_FPBV" || s == "QF_BVFP" || s == "QF_FPLRA" || logic_is_allcsp(s); } bool smt_logics::logic_has_uf(symbol const & s) { diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index 0e2128990..89421f079 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -3,7 +3,7 @@ Copyright (c) 2011 Microsoft Corporation Module Name: - solver.h + solver.cpp Abstract: @@ -21,25 +21,25 @@ Notes: #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/ast_pp_util.h" +#include "ast/display_dimacs.h" #include "tactic/model_converter.h" #include "solver/solver.h" +#include "solver/solver_params.hpp" #include "model/model_evaluator.h" unsigned solver::get_num_assertions() const { - NOT_IMPLEMENTED_YET(); + UNREACHABLE(); return 0; } expr * solver::get_assertion(unsigned idx) const { - NOT_IMPLEMENTED_YET(); + UNREACHABLE(); return nullptr; } std::ostream& solver::display(std::ostream & out, unsigned n, expr* const* assumptions) const { expr_ref_vector fmls(get_manager()); - stopwatch sw; - sw.start(); get_assertions(fmls); ast_pp_util visitor(get_manager()); model_converter_ref mc = get_model_converter(); @@ -57,6 +57,12 @@ std::ostream& solver::display(std::ostream & out, unsigned n, expr* const* assum return out; } +std::ostream& solver::display_dimacs(std::ostream& out) const { + expr_ref_vector fmls(get_manager()); + get_assertions(fmls); + return ::display_dimacs(out, fmls); +} + void solver::get_assertions(expr_ref_vector& fmls) const { unsigned sz = get_num_assertions(); for (unsigned i = 0; i < sz; ++i) { @@ -208,8 +214,6 @@ void solver::assert_expr(expr* f, expr* t) { expr_ref fml(f, m); expr_ref a(t, m); if (m_enforce_model_conversion) { - IF_VERBOSE(0, verbose_stream() << "enforce model conversion\n";); - exit(0); model_converter_ref mc = get_model_converter(); if (mc) { (*mc)(fml); @@ -219,22 +223,36 @@ void solver::assert_expr(expr* f, expr* t) { assert_expr_core2(fml, a); } +static void insert_ctrl_c(param_descrs & r) { + r.insert("ctrl_c", CPK_BOOL, "enable interrupts from ctrl-c", "false"); +} + + void solver::collect_param_descrs(param_descrs & r) { r.insert("solver.enforce_model_conversion", CPK_BOOL, "(default: false) enforce model conversion when asserting formulas"); + insert_timeout(r); + insert_rlimit(r); + insert_max_memory(r); + insert_ctrl_c(r); } void solver::reset_params(params_ref const & p) { m_params = p; - m_enforce_model_conversion = m_params.get_bool("solver.enforce_model_conversion", false); + solver_params sp(m_params); + m_enforce_model_conversion = sp.enforce_model_conversion(); + m_cancel_backup_file = sp.cancel_backup_file(); } void solver::updt_params(params_ref const & p) { m_params.copy(p); - m_enforce_model_conversion = m_params.get_bool("solver.enforce_model_conversion", false); + solver_params sp(m_params); + m_enforce_model_conversion = sp.enforce_model_conversion(); + m_cancel_backup_file = sp.cancel_backup_file(); } -expr_ref_vector solver::get_units(ast_manager& m) { +expr_ref_vector solver::get_units() { + ast_manager& m = get_manager(); expr_ref_vector fmls(m), result(m), tmp(m); get_assertions(fmls); obj_map units; @@ -267,7 +285,8 @@ expr_ref_vector solver::get_units(ast_manager& m) { } -expr_ref_vector solver::get_non_units(ast_manager& m) { +expr_ref_vector solver::get_non_units() { + ast_manager& m = get_manager(); expr_ref_vector result(m), fmls(m); get_assertions(fmls); family_id bfid = m.get_basic_family_id(); @@ -302,3 +321,32 @@ expr_ref_vector solver::get_non_units(ast_manager& m) { } return result; } + + +lbool solver::check_sat(unsigned num_assumptions, expr * const * assumptions) { + lbool r = l_undef; + try { + r = check_sat_core(num_assumptions, assumptions); + } + catch (...) { + if (get_manager().canceled()) { + dump_state(num_assumptions, assumptions); + } + throw; + } + if (r == l_undef && get_manager().canceled()) { + dump_state(num_assumptions, assumptions); + } + return r; +} + +void solver::dump_state(unsigned sz, expr* const* assumptions) { + if ((symbol::null != m_cancel_backup_file) && + !m_cancel_backup_file.is_numerical() && + m_cancel_backup_file.c_ptr() && + m_cancel_backup_file.bare_str()[0]) { + std::string file = m_cancel_backup_file.str(); + std::ofstream ous(file); + display(ous, sz, assumptions); + } +} diff --git a/src/solver/solver.h b/src/solver/solver.h index 7437a5a08..be3751bcd 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -44,8 +44,9 @@ public: - results based on check_sat_result API */ class solver : public check_sat_result { - params_ref m_params; - bool m_enforce_model_conversion; + params_ref m_params; + bool m_enforce_model_conversion; + symbol m_cancel_backup_file; public: solver(): m_enforce_model_conversion(false) {} ~solver() override {} @@ -140,7 +141,8 @@ public: If it is unsatisfiable, and unsat-core generation is enabled. Then, the unsat-core is a subset of these assumptions. */ - virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions) = 0; + + lbool check_sat(unsigned num_assumptions, expr * const * assumptions); lbool check_sat(expr_ref_vector const& asms) { return check_sat(asms.size(), asms.c_ptr()); } @@ -227,6 +229,11 @@ public: */ virtual std::ostream& display(std::ostream & out, unsigned n = 0, expr* const* assumptions = nullptr) const; + /** + \brief Display the content of this solver in DIMACS format + */ + std::ostream& display_dimacs(std::ostream & out) const; + /** \brief expose model converter when solver produces partially reduced set of assertions. */ @@ -236,9 +243,15 @@ public: /** \brief extract units from solver. */ - expr_ref_vector get_units(ast_manager& m); + expr_ref_vector get_units(); - expr_ref_vector get_non_units(ast_manager& m); + expr_ref_vector get_non_units(); + + virtual expr_ref_vector get_trail() = 0; // { return expr_ref_vector(get_manager()); } + + virtual void get_levels(ptr_vector const& vars, unsigned_vector& depth) = 0; + + virtual void set_activity(expr* lit, double activity) = 0; class scoped_push { solver& s; @@ -249,14 +262,17 @@ public: void disable_pop() { m_nopop = true; } }; + virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) = 0; protected: virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences); + void dump_state(unsigned sz, expr* const* assumptions); bool is_literal(ast_manager& m, expr* e); + }; typedef ref solver_ref; diff --git a/src/solver/solver_na2as.cpp b/src/solver/solver_na2as.cpp index a3fcd0e0b..6173dfddf 100644 --- a/src/solver/solver_na2as.cpp +++ b/src/solver/solver_na2as.cpp @@ -61,10 +61,10 @@ struct append_assumptions { } }; -lbool solver_na2as::check_sat(unsigned num_assumptions, expr * const * assumptions) { +lbool solver_na2as::check_sat_core(unsigned num_assumptions, expr * const * assumptions) { append_assumptions app(m_assumptions, num_assumptions, assumptions); TRACE("solver_na2as", display(tout);); - return check_sat_core(m_assumptions.size(), m_assumptions.c_ptr()); + return check_sat_core2(m_assumptions.size(), m_assumptions.c_ptr()); } lbool solver_na2as::check_sat_cc(const expr_ref_vector &assumptions, vector const &clauses) { diff --git a/src/solver/solver_na2as.h b/src/solver/solver_na2as.h index d1515a206..67ec004cc 100644 --- a/src/solver/solver_na2as.h +++ b/src/solver/solver_na2as.h @@ -35,10 +35,9 @@ public: ~solver_na2as() override; void assert_expr_core2(expr * t, expr * a) override; - // virtual void assert_expr_core(expr * t) = 0; // Subclasses of solver_na2as should redefine the following *_core methods instead of these ones. - lbool check_sat(unsigned num_assumptions, expr * const * assumptions) override; + lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override; lbool check_sat_cc(const expr_ref_vector &assumptions, vector const &clauses) override; void push() override; void pop(unsigned n) override; @@ -49,7 +48,7 @@ public: lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override; lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override; protected: - virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) = 0; + virtual lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) = 0; virtual lbool check_sat_cc_core(const expr_ref_vector &assumptions, vector const &clauses) { NOT_IMPLEMENTED_YET(); } virtual void push_core() = 0; virtual void pop_core(unsigned n) = 0; diff --git a/src/solver/solver_params.pyg b/src/solver/solver_params.pyg new file mode 100644 index 000000000..29690a164 --- /dev/null +++ b/src/solver/solver_params.pyg @@ -0,0 +1,8 @@ + +def_module_params('solver', + description='solver parameters', + export=True, + params=(('enforce_model_conversion', BOOL, False, "apply model transformation on new assertions"), + ('cancel_backup_file', SYMBOL, '', "file to save partial search state if search is canceled"), + )) + diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index 598bb6c02..4d6724aa7 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -119,7 +119,19 @@ public: } } - lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { + void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { + m_base->get_levels(vars, depth); + } + + expr_ref_vector get_trail() override { + return m_base->get_trail(); + } + + void set_activity(expr* var, double activity) override { + m_base->set_activity(var, activity); + } + + lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) override { SASSERT(!m_pushed || get_scope_level() > 0); m_proof.reset(); scoped_watch _t_(m_pool.m_check_watch); diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index 3a905bafd..c62e920e7 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -62,7 +62,7 @@ public: void push_core() override; void pop_core(unsigned n) override; - lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override; + lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) override; void collect_statistics(statistics & st) const override; void get_unsat_core(expr_ref_vector & r) override; @@ -85,6 +85,19 @@ public: model_converter_ref get_model_converter() const override { return m_mc; } + void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { + throw default_exception("cannot retrieve depth from solvers created using tactics"); + } + + expr_ref_vector get_trail() override { + throw default_exception("cannot retrieve trail from solvers created using tactcis"); + } + + void set_activity(expr* var, double activity) override { + throw default_exception("cannot set activity for solvers created using tactcis"); + } + + }; ast_manager& tactic2solver::get_manager() const { return m_assertions.get_manager(); } @@ -136,7 +149,7 @@ void tactic2solver::pop_core(unsigned n) { m_result = nullptr; } -lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * assumptions) { +lbool tactic2solver::check_sat_core2(unsigned num_assumptions, expr * const * assumptions) { if (m_tactic.get() == nullptr) return l_false; ast_manager & m = m_assertions.m(); @@ -170,7 +183,7 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass break; default: m_result->set_status(l_undef); - if (reason_unknown != "") + if (!reason_unknown.empty()) m_result->m_unknown = reason_unknown; if (num_assumptions == 0 && m_scopes.empty()) { m_assertions.reset(); diff --git a/src/tactic/CMakeLists.txt b/src/tactic/CMakeLists.txt index c9554b76a..495078afc 100644 --- a/src/tactic/CMakeLists.txt +++ b/src/tactic/CMakeLists.txt @@ -22,4 +22,6 @@ z3_add_component(tactic probe.h sine_filter.h tactic.h + PYG_FILES + tactic_params.pyg ) diff --git a/src/tactic/arith/nla2bv_tactic.cpp b/src/tactic/arith/nla2bv_tactic.cpp index 06150e18b..6662aec33 100644 --- a/src/tactic/arith/nla2bv_tactic.cpp +++ b/src/tactic/arith/nla2bv_tactic.cpp @@ -21,6 +21,7 @@ Notes: #include "tactic/tactical.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" +#include "ast/pb_decl_plugin.h" #include "ast/for_each_expr.h" #include "ast/rewriter/expr_replacer.h" #include "util/optional.h" @@ -85,13 +86,18 @@ class nla2bv_tactic : public tactic { TRACE("nla2bv", g.display(tout); tout << "Muls: " << count_mul(g) << "\n"; ); + tactic_report report("nla->bv", g); m_fmc = alloc(generic_model_converter, m_manager, "nla2bv"); m_bounds(g); collect_power2(g); - if(!collect_vars(g)) { + switch (collect_vars(g)) { + case has_num: + break; + case not_supported: throw tactic_exception("goal is not in the fragment supported by nla2bv"); + case is_bool: + return; } - tactic_report report("nla->bv", g); substitute_vars(g); TRACE("nla2bv", g.display(tout << "substitute vars\n");); reduce_bv2int(g); @@ -308,41 +314,47 @@ class nla2bv_tactic : public tactic { class get_uninterp_proc { imp& m_imp; + arith_util& a; + ast_manager& m; + pb_util pb; ptr_vector m_vars; + bool m_no_arith; bool m_in_supported_fragment; public: - get_uninterp_proc(imp& s): m_imp(s), m_in_supported_fragment(true) {} + get_uninterp_proc(imp& s): m_imp(s), a(s.m_arith), m(a.get_manager()), pb(m), m_no_arith(true), m_in_supported_fragment(true) {} ptr_vector const& vars() { return m_vars; } + bool no_arith() const { return m_no_arith; } void operator()(var * n) { m_in_supported_fragment = false; } void operator()(app* n) { - arith_util& a = m_imp.m_arith; - ast_manager& m = a.get_manager(); - if (a.is_int(n) && - is_uninterp_const(n)) { + if (a.is_int(n) && is_uninterp_const(n)) { m_vars.push_back(n); } - else if (a.is_real(n) && - is_uninterp_const(n)) { + else if (a.is_real(n) && is_uninterp_const(n)) { m_vars.push_back(n); } else if (m.is_bool(n) && is_uninterp_const(n)) { } - else if (!(a.is_mul(n) || - a.is_add(n) || - a.is_sub(n) || - a.is_le(n) || - a.is_lt(n) || - a.is_ge(n) || - a.is_gt(n) || - a.is_numeral(n) || - a.is_uminus(n) || - m_imp.m_bv2real.is_pos_le(n) || - m_imp.m_bv2real.is_pos_lt(n) || - n->get_family_id() == a.get_manager().get_basic_family_id())) { - TRACE("nla2bv", tout << "Not supported: " << mk_ismt2_pp(n, a.get_manager()) << "\n";); + else if (m.is_bool(n) && n->get_decl()->get_family_id() == pb.get_family_id()) { + + } + else if (a.is_mul(n) || + a.is_add(n) || + a.is_sub(n) || + a.is_le(n) || + a.is_lt(n) || + a.is_ge(n) || + a.is_gt(n) || + a.is_numeral(n) || + a.is_uminus(n) || + m_imp.m_bv2real.is_pos_le(n) || + m_imp.m_bv2real.is_pos_lt(n)) { + m_no_arith = false; + } + else if (n->get_family_id() != m.get_basic_family_id()) { + TRACE("nla2bv", tout << "Not supported: " << mk_ismt2_pp(n, m) << "\n";); m_in_supported_fragment = false; } m_imp.update_num_bits(n); @@ -353,13 +365,17 @@ class nla2bv_tactic : public tactic { bool is_supported() const { return m_in_supported_fragment; } }; - bool collect_vars(goal const & g) { + enum collect_t { has_num, not_supported, is_bool }; + + collect_t collect_vars(goal const & g) { get_uninterp_proc fe_var(*this); for_each_expr_at(fe_var, g); for (unsigned i = 0; i < fe_var.vars().size(); ++i) { add_var(fe_var.vars()[i]); } - return fe_var.is_supported() && !fe_var.vars().empty(); + if (!fe_var.is_supported()) return not_supported; + if (fe_var.vars().empty() && fe_var.no_arith()) return is_bool; + return has_num; } class count_mul_proc { diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp index 67dadd34b..14b6c6b41 100644 --- a/src/tactic/arith/purify_arith_tactic.cpp +++ b/src/tactic/arith/purify_arith_tactic.cpp @@ -509,6 +509,9 @@ struct purify_arith_proc { return BR_DONE; } else { + expr_ref s(u().mk_sin(theta), m()); + expr_ref c(u().mk_cos(theta), m()); + push_cnstr(EQ(mk_real_one(), u().mk_add(u().mk_mul(s, s), u().mk_mul(c, c)))); return BR_FAILED; } } @@ -777,11 +780,10 @@ struct purify_arith_proc { if (produce_models && !m_sin_cos.empty()) { generic_model_converter* emc = alloc(generic_model_converter, m(), "purify_sin_cos"); mc = concat(mc.get(), emc); - obj_map >::iterator it = m_sin_cos.begin(), end = m_sin_cos.end(); - for (; it != end; ++it) { - emc->add(it->m_key->get_decl(), - m().mk_ite(u().mk_ge(it->m_value.first, mk_real_zero()), u().mk_acos(it->m_value.second), - u().mk_add(u().mk_acos(u().mk_uminus(it->m_value.second)), u().mk_pi()))); + for (auto const& kv : m_sin_cos) { + emc->add(kv.m_key->get_decl(), + m().mk_ite(u().mk_ge(kv.m_value.first, mk_real_zero()), u().mk_acos(kv.m_value.second), + u().mk_add(u().mk_acos(u().mk_uminus(kv.m_value.second)), u().mk_pi()))); } } diff --git a/src/tactic/bv/bv_bound_chk_tactic.cpp b/src/tactic/bv/bv_bound_chk_tactic.cpp index 75b772720..cf35afef4 100644 --- a/src/tactic/bv/bv_bound_chk_tactic.cpp +++ b/src/tactic/bv/bv_bound_chk_tactic.cpp @@ -74,7 +74,7 @@ struct bv_bound_chk_rewriter_cfg : public default_rewriter_cfg { bv_bounds bvb(m()); const br_status rv = bvb.rewrite(m_bv_ineq_consistency_test_max, f, num, args, result); if (rv != BR_FAILED && (m_m.is_false(result) || m_m.is_true(result))) m_stats.m_unsats++; - else if (rv != BR_FAILED && bvb.singletons().size()) m_stats.m_singletons++; + else if (rv != BR_FAILED && !bvb.singletons().empty()) m_stats.m_singletons++; else if (rv != BR_FAILED && is_app(result) && to_app(result)->get_num_args() < num) m_stats.m_reduces++; return rv; } diff --git a/src/tactic/bv/bv_size_reduction_tactic.cpp b/src/tactic/bv/bv_size_reduction_tactic.cpp index 964102825..fd1d1499b 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.cpp +++ b/src/tactic/bv/bv_size_reduction_tactic.cpp @@ -27,28 +27,8 @@ Notes: #include "tactic/generic_model_converter.h" #include "ast/ast_smt2_pp.h" +namespace { class bv_size_reduction_tactic : public tactic { - struct imp; - imp * m_imp; -public: - bv_size_reduction_tactic(ast_manager & m); - - tactic * translate(ast_manager & m) override { - return alloc(bv_size_reduction_tactic, m); - } - - ~bv_size_reduction_tactic() override; - - void operator()(goal_ref const & g, goal_ref_buffer & result) override; - - void cleanup() override; -}; - -tactic * mk_bv_size_reduction_tactic(ast_manager & m, params_ref const & p) { - return clean(alloc(bv_size_reduction_tactic, m)); -} - -struct bv_size_reduction_tactic::imp { typedef rational numeral; typedef generic_model_converter bv_size_reduction_mc; @@ -63,12 +43,29 @@ struct bv_size_reduction_tactic::imp { scoped_ptr m_replacer; bool m_produce_models; - imp(ast_manager & _m): - m(_m), +public: + bv_size_reduction_tactic(ast_manager & m) : + m(m), m_util(m), m_replacer(mk_default_expr_replacer(m)) { } + tactic * translate(ast_manager & m) override { + return alloc(bv_size_reduction_tactic, m); + } + + void operator()(goal_ref const & g, goal_ref_buffer & result) override; + + void cleanup() override { + m_signed_lowers.reset(); + m_signed_uppers.reset(); + m_unsigned_lowers.reset(); + m_unsigned_uppers.reset(); + m_mc = nullptr; + m_fmc = nullptr; + m_replacer->reset(); + } + void update_signed_lower(app * v, numeral const & k) { // k <= v obj_map::obj_map_entry * entry = m_signed_lowers.insert_if_not_there2(v, k); @@ -178,7 +175,7 @@ struct bv_size_reduction_tactic::imp { throw tactic_exception(m.limit().get_cancel_msg()); } - void operator()(goal & g, model_converter_ref & mc) { + void run(goal & g, model_converter_ref & mc) { if (g.inconsistent()) return; TRACE("before_bv_size_reduction", g.display(tout);); @@ -373,14 +370,6 @@ struct bv_size_reduction_tactic::imp { }; -bv_size_reduction_tactic::bv_size_reduction_tactic(ast_manager & m) { - m_imp = alloc(imp, m); -} - -bv_size_reduction_tactic::~bv_size_reduction_tactic() { - dealloc(m_imp); -} - void bv_size_reduction_tactic::operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); @@ -388,17 +377,14 @@ void bv_size_reduction_tactic::operator()(goal_ref const & g, fail_if_unsat_core_generation("bv-size-reduction", g); result.reset(); model_converter_ref mc; - m_imp->operator()(*(g.get()), mc); + run(*(g.get()), mc); g->inc_depth(); g->add(mc.get()); result.push_back(g.get()); SASSERT(g->is_well_sorted()); } - - -void bv_size_reduction_tactic::cleanup() { - ast_manager & m = m_imp->m; - m_imp->~imp(); - new (m_imp) imp(m); } +tactic * mk_bv_size_reduction_tactic(ast_manager & m, params_ref const & p) { + return clean(alloc(bv_size_reduction_tactic, m)); +} diff --git a/src/tactic/bv/bv_size_reduction_tactic.h b/src/tactic/bv/bv_size_reduction_tactic.h index 4a24a1d78..1bb512f3f 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.h +++ b/src/tactic/bv/bv_size_reduction_tactic.h @@ -21,8 +21,7 @@ Author: Notes: --*/ -#ifndef BV_SIZE_REDUCTION_TACTIC_H_ -#define BV_SIZE_REDUCTION_TACTIC_H_ +#pragma once #include "util/params.h" class ast_manager; @@ -32,5 +31,3 @@ tactic * mk_bv_size_reduction_tactic(ast_manager & m, params_ref const & p = par /* ADD_TACTIC("reduce-bv-size", "try to reduce bit-vector sizes using inequalities.", "mk_bv_size_reduction_tactic(m, p)") */ - -#endif diff --git a/src/tactic/bv/elim_small_bv_tactic.cpp b/src/tactic/bv/elim_small_bv_tactic.cpp index 0b5b98308..256586896 100644 --- a/src/tactic/bv/elim_small_bv_tactic.cpp +++ b/src/tactic/bv/elim_small_bv_tactic.cpp @@ -28,6 +28,7 @@ Revision History: #include "tactic/bv/elim_small_bv_tactic.h" +namespace { class elim_small_bv_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { @@ -36,7 +37,6 @@ class elim_small_bv_tactic : public tactic { bv_util m_util; th_rewriter m_simp; ref m_mc; - goal * m_goal; unsigned m_max_bits; unsigned long long m_max_steps; unsigned long long m_max_memory; // in bytes @@ -52,7 +52,6 @@ class elim_small_bv_tactic : public tactic { m_bindings(_m), m_num_eliminated(0) { updt_params(p); - m_goal = nullptr; m_max_steps = UINT_MAX; } @@ -75,7 +74,7 @@ class elim_small_bv_tactic : public tactic { TRACE("elim_small_bv", tout << "replace idx " << idx << " with " << mk_ismt2_pp(replacement, m) << " in " << mk_ismt2_pp(e, m) << std::endl;); expr_ref res(m); - expr_ref_vector substitution(m); + ptr_vector substitution; substitution.resize(num_decls, nullptr); substitution[num_decls - idx - 1] = replacement; @@ -93,7 +92,7 @@ class elim_small_bv_tactic : public tactic { TRACE("elim_small_bv", tout << "substitution: " << std::endl; for (unsigned k = 0; k < substitution.size(); k++) { - expr * se = substitution[k].get(); + expr * se = substitution[k]; tout << k << " = "; if (se == 0) tout << "0"; else tout << mk_ismt2_pp(se, m); @@ -151,9 +150,7 @@ class elim_small_bv_tactic : public tactic { expr_ref_vector new_bodies(m); for (unsigned j = 0; j < bv_sz && !max_steps_exceeded(num_steps); j ++) { expr_ref n(m_util.mk_numeral(j, bv_sz), m); - expr_ref nb(m); - nb = replace_var(uv, num_decls, max_var_idx_p1, i, s, body, n); - new_bodies.push_back(nb); + new_bodies.push_back(replace_var(uv, num_decls, max_var_idx_p1, i, s, body, n)); num_steps++; } @@ -214,69 +211,24 @@ class elim_small_bv_tactic : public tactic { } }; - struct imp { - ast_manager & m; - rw m_rw; + ast_manager & m; + rw m_rw; + params_ref m_params; - imp(ast_manager & _m, params_ref const & p) : - m(_m), - m_rw(m, p) { - } - - void updt_params(params_ref const & p) { - m_rw.cfg().updt_params(p); - } - - void operator()(goal_ref const & g, goal_ref_buffer & result) { - SASSERT(g->is_well_sorted()); - tactic_report report("elim-small-bv", *g); - bool produce_proofs = g->proofs_enabled(); - fail_if_proof_generation("elim-small-bv", g); - fail_if_unsat_core_generation("elim-small-bv", g); - m_rw.cfg().m_produce_models = g->models_enabled(); - - m_rw.m_cfg.m_goal = g.get(); - expr_ref new_curr(m); - proof_ref new_pr(m); - unsigned size = g->size(); - for (unsigned idx = 0; idx < size; idx++) { - expr * curr = g->form(idx); - m_rw(curr, new_curr, new_pr); - if (produce_proofs) { - proof * pr = g->pr(idx); - new_pr = m.mk_modus_ponens(pr, new_pr); - } - g->update(idx, new_curr, new_pr, g->dep(idx)); - } - g->add(m_rw.m_cfg.m_mc.get()); - - report_tactic_progress(":elim-small-bv-num-eliminated", m_rw.m_cfg.m_num_eliminated); - g->inc_depth(); - result.push_back(g.get()); - TRACE("elim-small-bv", g->display(tout);); - SASSERT(g->is_well_sorted()); - } - }; - - imp * m_imp; - params_ref m_params; public: elim_small_bv_tactic(ast_manager & m, params_ref const & p) : + m(m), + m_rw(m, p), m_params(p) { - m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(elim_small_bv_tactic, m, m_params); } - ~elim_small_bv_tactic() override { - dealloc(m_imp); - } - void updt_params(params_ref const & p) override { m_params = p; - m_imp->m_rw.cfg().updt_params(p); + m_rw.cfg().updt_params(p); } void collect_param_descrs(param_descrs & r) override { @@ -285,18 +237,43 @@ public: r.insert("max_bits", CPK_UINT, "(default: 4) maximum bit-vector size of quantified bit-vectors to be eliminated."); } - void operator()(goal_ref const & in, + void operator()(goal_ref const & g, goal_ref_buffer & result) override { - (*m_imp)(in, result); + SASSERT(g->is_well_sorted()); + tactic_report report("elim-small-bv", *g); + bool produce_proofs = g->proofs_enabled(); + fail_if_proof_generation("elim-small-bv", g); + fail_if_unsat_core_generation("elim-small-bv", g); + m_rw.cfg().m_produce_models = g->models_enabled(); + + expr_ref new_curr(m); + proof_ref new_pr(m); + unsigned size = g->size(); + for (unsigned idx = 0; idx < size; idx++) { + expr * curr = g->form(idx); + m_rw(curr, new_curr, new_pr); + if (produce_proofs) { + proof * pr = g->pr(idx); + new_pr = m.mk_modus_ponens(pr, new_pr); + } + g->update(idx, new_curr, new_pr, g->dep(idx)); + } + g->add(m_rw.m_cfg.m_mc.get()); + + report_tactic_progress(":elim-small-bv-num-eliminated", m_rw.m_cfg.m_num_eliminated); + g->inc_depth(); + result.push_back(g.get()); + TRACE("elim-small-bv", g->display(tout);); + SASSERT(g->is_well_sorted()); } void cleanup() override { - ast_manager & m = m_imp->m; - m_imp->~imp(); - m_imp = new (m_imp) imp(m, m_params); + m_rw.~rw(); + new (&m_rw) rw(m, m_params); } }; +} tactic * mk_elim_small_bv_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(elim_small_bv_tactic, m, p)); diff --git a/src/tactic/bv/elim_small_bv_tactic.h b/src/tactic/bv/elim_small_bv_tactic.h index 675ec3de7..e4a91f70f 100644 --- a/src/tactic/bv/elim_small_bv_tactic.h +++ b/src/tactic/bv/elim_small_bv_tactic.h @@ -16,8 +16,7 @@ Author: Revision History: --*/ -#ifndef ELIM_SMALL_BV_H_ -#define ELIM_SMALL_BV_H_ +#pragma once #include "util/params.h" class ast_manager; @@ -28,5 +27,3 @@ tactic * mk_elim_small_bv_tactic(ast_manager & m, params_ref const & p = params_ /* ADD_TACTIC("elim-small-bv", "eliminate small, quantified bit-vectors by expansion.", "mk_elim_small_bv_tactic(m, p)") */ - -#endif diff --git a/src/tactic/core/blast_term_ite_tactic.cpp b/src/tactic/core/blast_term_ite_tactic.cpp index eefb9418e..259b479c5 100644 --- a/src/tactic/core/blast_term_ite_tactic.cpp +++ b/src/tactic/core/blast_term_ite_tactic.cpp @@ -16,11 +16,12 @@ Author: Notes: --*/ -#include "tactic/tactical.h" +#include "util/cooperate.h" #include "ast/normal_forms/defined_names.h" #include "ast/rewriter/rewriter_def.h" -#include "util/cooperate.h" #include "ast/scoped_proof.h" +#include "tactic/tactical.h" +#include "tactic/tactic_params.hpp" @@ -50,9 +51,10 @@ class blast_term_ite_tactic : public tactic { } void updt_params(params_ref const & p) { + tactic_params tp(p); m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); - m_max_steps = p.get_uint("max_steps", UINT_MAX); - m_max_inflation = p.get_uint("max_inflation", UINT_MAX); // multiplicative factor of initial term size. + m_max_steps = p.get_uint("max_steps", tp.blast_term_ite_max_steps()); + m_max_inflation = p.get_uint("max_inflation", tp.blast_term_ite_max_inflation()); // multiplicative factor of initial term size. } @@ -182,8 +184,7 @@ public: void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); - r.insert("max_args", CPK_UINT, - "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); + r.insert("max_inflation", CPK_UINT, "(default: infinity) multiplicative factor of initial term size."); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { diff --git a/src/tactic/core/distribute_forall_tactic.cpp b/src/tactic/core/distribute_forall_tactic.cpp index c2688f741..800644d9e 100644 --- a/src/tactic/core/distribute_forall_tactic.cpp +++ b/src/tactic/core/distribute_forall_tactic.cpp @@ -49,8 +49,7 @@ class distribute_forall_tactic : public tactic { expr * not_arg = m.mk_not(arg); quantifier_ref tmp_q(m); tmp_q = m.update_quantifier(old_q, not_arg); - expr_ref new_q = elim_unused_vars(m, tmp_q, params_ref()); - new_args.push_back(new_q); + new_args.push_back(elim_unused_vars(m, tmp_q, params_ref())); } result = m.mk_and(new_args.size(), new_args.c_ptr()); return true; @@ -68,8 +67,7 @@ class distribute_forall_tactic : public tactic { expr * arg = to_app(new_body)->get_arg(i); quantifier_ref tmp_q(m); tmp_q = m.update_quantifier(old_q, arg); - expr_ref new_q = elim_unused_vars(m, tmp_q, params_ref()); - new_args.push_back(new_q); + new_args.push_back(elim_unused_vars(m, tmp_q, params_ref())); } result = m.mk_and(new_args.size(), new_args.c_ptr()); return true; diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 577db30cd..5b77fb9a2 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -28,881 +28,864 @@ Notes: #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" +namespace { class elim_uncnstr_tactic : public tactic { - struct imp { - // unconstrained vars collector + // unconstrained vars collector - typedef generic_model_converter mc; + typedef generic_model_converter mc; - struct rw_cfg : public default_rewriter_cfg { - bool m_produce_proofs; - obj_hashtable & m_vars; - ref m_mc; - arith_util m_a_util; - bv_util m_bv_util; - array_util m_ar_util; - datatype_util m_dt_util; - app_ref_vector m_fresh_vars; - obj_map m_cache; - app_ref_vector m_cache_domain; - unsigned long long m_max_memory; - unsigned m_max_steps; - - rw_cfg(ast_manager & m, bool produce_proofs, obj_hashtable & vars, mc * _m, - unsigned long long max_memory, unsigned max_steps): - m_produce_proofs(produce_proofs), - m_vars(vars), - m_mc(_m), - m_a_util(m), - m_bv_util(m), - m_ar_util(m), - m_dt_util(m), - m_fresh_vars(m), - m_cache_domain(m), - m_max_memory(max_memory), - m_max_steps(max_steps) { - } - - ast_manager & m() const { return m_a_util.get_manager(); } - - bool max_steps_exceeded(unsigned num_steps) const { - cooperate("elim-uncnstr-vars"); - if (memory::get_allocation_size() > m_max_memory) - throw tactic_exception(TACTIC_MAX_MEMORY_MSG); - return num_steps > m_max_steps; - } - - bool uncnstr(expr * arg) const { - return m_vars.contains(arg); - } - - bool uncnstr(unsigned num, expr * const * args) const { - for (unsigned i = 0; i < num; i++) - if (!uncnstr(args[i])) - return false; - return true; - } - - /** - \brief Create a fresh variable for abstracting (f args[0] ... args[num-1]) - Return true if it a new variable was created, and false if the variable already existed for this - application. Store the variable in v - */ - bool mk_fresh_uncnstr_var_for(app * t, app * & v) { - if (m_cache.find(t, v)) { - return false; // variable already existed for this application - } - - v = m().mk_fresh_const(nullptr, m().get_sort(t)); - TRACE("elim_uncnstr_bug", tout << "eliminating:\n" << mk_ismt2_pp(t, m()) << "\n";); - TRACE("elim_uncnstr_bug_ll", tout << "eliminating:\n" << mk_bounded_pp(t, m()) << "\n";); - m_fresh_vars.push_back(v); - if (m_mc) m_mc->hide(v); - m_cache_domain.push_back(t); - m_cache.insert(t, v); - return true; - } - - bool mk_fresh_uncnstr_var_for(func_decl * f, unsigned num, expr * const * args, app * & v) { - return mk_fresh_uncnstr_var_for(m().mk_app(f, num, args), v); - } - - bool mk_fresh_uncnstr_var_for(func_decl * f, expr * arg1, expr * arg2, app * & v) { - return mk_fresh_uncnstr_var_for(m().mk_app(f, arg1, arg2), v); - } - - bool mk_fresh_uncnstr_var_for(func_decl * f, expr * arg, app * & v) { - return mk_fresh_uncnstr_var_for(m().mk_app(f, arg), v); - } - - void add_def(expr * v, expr * def) { - SASSERT(uncnstr(v)); - SASSERT(to_app(v)->get_num_args() == 0); - if (m_mc) - m_mc->add(to_app(v)->get_decl(), def); - } - - void add_defs(unsigned num, expr * const * args, expr * u, expr * identity) { - if (m_mc) { - add_def(args[0], u); - for (unsigned i = 1; i < num; i++) - add_def(args[i], identity); - } - } - - // return a term that is different from t. - bool mk_diff(expr * t, expr_ref & r) { - sort * s = m().get_sort(t); - if (m().is_bool(s)) { - r = m().mk_not(t); - return true; - } - family_id fid = s->get_family_id(); - if (fid == m_a_util.get_family_id()) { - r = m_a_util.mk_add(t, m_a_util.mk_numeral(rational(1), s)); - return true; - } - if (fid == m_bv_util.get_family_id()) { - r = m().mk_app(m_bv_util.get_family_id(), OP_BNOT, t); - return true; - } - if (fid == m_ar_util.get_family_id()) { - if (m().is_uninterp(get_array_range(s))) - return false; - unsigned arity = get_array_arity(s); - for (unsigned i = 0; i < arity; i++) - if (m().is_uninterp(get_array_domain(s, i))) - return false; - // building - // r = (store t i1 ... in d) - // where i1 ... in are arbitrary values - // and d is a term different from (select t i1 ... in) - ptr_buffer new_args; - new_args.push_back(t); - for (unsigned i = 0; i < arity; i++) - new_args.push_back(m().get_some_value(get_array_domain(s, i))); - expr_ref sel(m()); - sel = m().mk_app(fid, OP_SELECT, new_args.size(), new_args.c_ptr()); - expr_ref diff_sel(m()); - if (!mk_diff(sel, diff_sel)) - return false; - new_args.push_back(diff_sel); - r = m().mk_app(fid, OP_STORE, new_args.size(), new_args.c_ptr()); - return true; - } - if (fid == m_dt_util.get_family_id()) { - // In the current implementation, I only handle the case where - // the datatype has a recursive constructor. - ptr_vector const & constructors = *m_dt_util.get_datatype_constructors(s); - for (func_decl * constructor : constructors) { - unsigned num = constructor->get_arity(); - unsigned target = UINT_MAX; - for (unsigned i = 0; i < num; i++) { - sort * s_arg = constructor->get_domain(i); - if (s == s_arg) { - target = i; - continue; - } - if (m().is_uninterp(s_arg)) - break; - } - if (target == UINT_MAX) - continue; - // use the constructor the distinct term constructor(...,t,...) - ptr_buffer new_args; - for (unsigned i = 0; i < num; i++) { - if (i == target) { - new_args.push_back(t); - } - else { - new_args.push_back(m().get_some_value(constructor->get_domain(i))); - } - } - r = m().mk_app(constructor, new_args.size(), new_args.c_ptr()); - return true; - } - // TODO: handle more cases. + struct rw_cfg : public default_rewriter_cfg { + bool m_produce_proofs; + obj_hashtable & m_vars; + ref m_mc; + arith_util m_a_util; + bv_util m_bv_util; + array_util m_ar_util; + datatype_util m_dt_util; + app_ref_vector m_fresh_vars; + obj_map m_cache; + app_ref_vector m_cache_domain; + unsigned long long m_max_memory; + unsigned m_max_steps; + + rw_cfg(ast_manager & m, bool produce_proofs, obj_hashtable & vars, mc * _m, + unsigned long long max_memory, unsigned max_steps): + m_produce_proofs(produce_proofs), + m_vars(vars), + m_mc(_m), + m_a_util(m), + m_bv_util(m), + m_ar_util(m), + m_dt_util(m), + m_fresh_vars(m), + m_cache_domain(m), + m_max_memory(max_memory), + m_max_steps(max_steps) { + } + + ast_manager & m() const { return m_a_util.get_manager(); } + + bool max_steps_exceeded(unsigned num_steps) const { + cooperate("elim-uncnstr-vars"); + if (memory::get_allocation_size() > m_max_memory) + throw tactic_exception(TACTIC_MAX_MEMORY_MSG); + return num_steps > m_max_steps; + } + + bool uncnstr(expr * arg) const { + return m_vars.contains(arg); + } + + bool uncnstr(unsigned num, expr * const * args) const { + for (unsigned i = 0; i < num; i++) + if (!uncnstr(args[i])) return false; + return true; + } + + /** + \brief Create a fresh variable for abstracting (f args[0] ... args[num-1]) + Return true if it a new variable was created, and false if the variable already existed for this + application. Store the variable in v + */ + bool mk_fresh_uncnstr_var_for(app * t, app * & v) { + if (m_cache.find(t, v)) { + return false; // variable already existed for this application + } + + v = m().mk_fresh_const(nullptr, m().get_sort(t)); + TRACE("elim_uncnstr_bug", tout << "eliminating:\n" << mk_ismt2_pp(t, m()) << "\n";); + TRACE("elim_uncnstr_bug_ll", tout << "eliminating:\n" << mk_bounded_pp(t, m()) << "\n";); + m_fresh_vars.push_back(v); + if (m_mc) m_mc->hide(v); + m_cache_domain.push_back(t); + m_cache.insert(t, v); + return true; + } + + bool mk_fresh_uncnstr_var_for(func_decl * f, unsigned num, expr * const * args, app * & v) { + return mk_fresh_uncnstr_var_for(m().mk_app(f, num, args), v); + } + + bool mk_fresh_uncnstr_var_for(func_decl * f, expr * arg1, expr * arg2, app * & v) { + return mk_fresh_uncnstr_var_for(m().mk_app(f, arg1, arg2), v); + } + + bool mk_fresh_uncnstr_var_for(func_decl * f, expr * arg, app * & v) { + return mk_fresh_uncnstr_var_for(m().mk_app(f, arg), v); + } + + void add_def(expr * v, expr * def) { + SASSERT(uncnstr(v)); + SASSERT(to_app(v)->get_num_args() == 0); + if (m_mc) + m_mc->add(to_app(v)->get_decl(), def); + } + + void add_defs(unsigned num, expr * const * args, expr * u, expr * identity) { + if (m_mc) { + add_def(args[0], u); + for (unsigned i = 1; i < num; i++) + add_def(args[i], identity); + } + } + + // return a term that is different from t. + bool mk_diff(expr * t, expr_ref & r) { + sort * s = m().get_sort(t); + if (m().is_bool(s)) { + r = m().mk_not(t); + return true; + } + family_id fid = s->get_family_id(); + if (fid == m_a_util.get_family_id()) { + r = m_a_util.mk_add(t, m_a_util.mk_numeral(rational(1), s)); + return true; + } + if (fid == m_bv_util.get_family_id()) { + r = m().mk_app(m_bv_util.get_family_id(), OP_BNOT, t); + return true; + } + if (fid == m_ar_util.get_family_id()) { + if (m().is_uninterp(get_array_range(s))) + return false; + unsigned arity = get_array_arity(s); + for (unsigned i = 0; i < arity; i++) + if (m().is_uninterp(get_array_domain(s, i))) + return false; + // building + // r = (store t i1 ... in d) + // where i1 ... in are arbitrary values + // and d is a term different from (select t i1 ... in) + ptr_buffer new_args; + new_args.push_back(t); + for (unsigned i = 0; i < arity; i++) + new_args.push_back(m().get_some_value(get_array_domain(s, i))); + expr_ref sel(m()); + sel = m().mk_app(fid, OP_SELECT, new_args.size(), new_args.c_ptr()); + expr_ref diff_sel(m()); + if (!mk_diff(sel, diff_sel)) + return false; + new_args.push_back(diff_sel); + r = m().mk_app(fid, OP_STORE, new_args.size(), new_args.c_ptr()); + return true; + } + if (fid == m_dt_util.get_family_id()) { + // In the current implementation, I only handle the case where + // the datatype has a recursive constructor. + ptr_vector const & constructors = *m_dt_util.get_datatype_constructors(s); + for (func_decl * constructor : constructors) { + unsigned num = constructor->get_arity(); + unsigned target = UINT_MAX; + for (unsigned i = 0; i < num; i++) { + sort * s_arg = constructor->get_domain(i); + if (s == s_arg) { + target = i; + continue; + } + if (m().is_uninterp(s_arg)) + break; + } + if (target == UINT_MAX) + continue; + // use the constructor the distinct term constructor(...,t,...) + ptr_buffer new_args; + for (unsigned i = 0; i < num; i++) { + if (i == target) { + new_args.push_back(t); + } + else { + new_args.push_back(m().get_some_value(constructor->get_domain(i))); + } + } + r = m().mk_app(constructor, new_args.size(), new_args.c_ptr()); + return true; } + // TODO: handle more cases. return false; } + return false; + } - app * process_eq(func_decl * f, expr * arg1, expr * arg2) { - expr * v; - expr * t; - if (uncnstr(arg1)) { - v = arg1; - t = arg2; - } - else if (uncnstr(arg2)) { - v = arg2; - t = arg1; - } - else { - return nullptr; - } - - sort * s = m().get_sort(arg1); - - // Remark: - // I currently do not support unconstrained vars that have - // uninterpreted sorts, for the following reasons: - // - Soundness - // (forall ((x S) (y S)) (= x y)) - // (not (= c1 c2)) - // - // The constants c1 and c2 have only one occurrence in - // the formula above, but they are not really unconstrained. - // The quantifier forces S to have interpretations of size 1. - // If we replace (= c1 c2) with fresh k. The formula will - // become satisfiable. - // - // - Even if the formula is quantifier free, I would still - // have to build an interpretation for the eliminated - // variables. - // - if (!m().is_fully_interp(s)) - return nullptr; - - // If the interpreted sort has only one element, - // then it is unsound to eliminate the unconstrained variable in the equality - sort_size sz = s->get_num_elements(); - - if (sz.is_finite() && sz.size() <= 1) - return nullptr; - - if (!m_mc) { - // easy case, model generation is disabled. - app * u; - mk_fresh_uncnstr_var_for(f, arg1, arg2, u); - return u; - } - - expr_ref d(m()); - if (mk_diff(t, d)) { - app * u; - if (!mk_fresh_uncnstr_var_for(f, arg1, arg2, u)) - return u; - add_def(v, m().mk_ite(u, t, d)); - return u; - } + app * process_eq(func_decl * f, expr * arg1, expr * arg2) { + expr * v; + expr * t; + if (uncnstr(arg1)) { + v = arg1; + t = arg2; + } + else if (uncnstr(arg2)) { + v = arg2; + t = arg1; + } + else { return nullptr; } - app * process_basic_app(func_decl * f, unsigned num, expr * const * args) { - SASSERT(f->get_family_id() == m().get_basic_family_id()); - switch (f->get_decl_kind()) { - case OP_ITE: - SASSERT(num == 3); - if (uncnstr(args[1]) && uncnstr(args[2])) { - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - add_def(args[1], r); - add_def(args[2], r); - return r; - } - if (uncnstr(args[0]) && uncnstr(args[1])) { - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - add_def(args[0], m().mk_true()); - add_def(args[1], r); - return r; - } - if (uncnstr(args[0]) && uncnstr(args[2])) { - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - add_def(args[0], m().mk_false()); - add_def(args[2], r); - return r; - } - return nullptr; - case OP_NOT: - SASSERT(num == 1); - if (uncnstr(args[0])) { - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - if (m_mc) - add_def(args[0], m().mk_not(r)); - return r; - } - return nullptr; - case OP_AND: - if (num > 0 && uncnstr(num, args)) { - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - if (m_mc) - add_defs(num, args, r, m().mk_true()); - return r; - } - return nullptr; - case OP_OR: - if (num > 0 && uncnstr(num, args)) { - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - if (m_mc) - add_defs(num, args, r, m().mk_false()); - return r; - } - return nullptr; - case OP_EQ: - SASSERT(num == 2); - return process_eq(f, args[0], args[1]); - default: - return nullptr; - } + sort * s = m().get_sort(arg1); + + // Remark: + // I currently do not support unconstrained vars that have + // uninterpreted sorts, for the following reasons: + // - Soundness + // (forall ((x S) (y S)) (= x y)) + // (not (= c1 c2)) + // + // The constants c1 and c2 have only one occurrence in + // the formula above, but they are not really unconstrained. + // The quantifier forces S to have interpretations of size 1. + // If we replace (= c1 c2) with fresh k. The formula will + // become satisfiable. + // + // - Even if the formula is quantifier free, I would still + // have to build an interpretation for the eliminated + // variables. + // + if (!m().is_fully_interp(s)) + return nullptr; + + // If the interpreted sort has only one element, + // then it is unsound to eliminate the unconstrained variable in the equality + sort_size sz = s->get_num_elements(); + + if (sz.is_finite() && sz.size() <= 1) + return nullptr; + + if (!m_mc) { + // easy case, model generation is disabled. + app * u; + mk_fresh_uncnstr_var_for(f, arg1, arg2, u); + return u; } - - app * process_le_ge(func_decl * f, expr * arg1, expr * arg2, bool le) { - expr * v; - expr * t; - if (uncnstr(arg1)) { - v = arg1; - t = arg2; - } - else if (uncnstr(arg2)) { - v = arg2; - t = arg1; - le = !le; - } - else { - return nullptr; - } + + expr_ref d(m()); + if (mk_diff(t, d)) { app * u; if (!mk_fresh_uncnstr_var_for(f, arg1, arg2, u)) return u; - if (!m_mc) - return u; - // v = ite(u, t, t + 1) if le - // v = ite(u, t, t - 1) if !le - add_def(v, m().mk_ite(u, t, m_a_util.mk_add(t, m_a_util.mk_numeral(rational(le ? 1 : -1), m().get_sort(arg1))))); + add_def(v, m().mk_ite(u, t, d)); return u; } + return nullptr; + } + + app * process_basic_app(func_decl * f, unsigned num, expr * const * args) { + SASSERT(f->get_family_id() == m().get_basic_family_id()); + switch (f->get_decl_kind()) { + case OP_ITE: + SASSERT(num == 3); + if (uncnstr(args[1]) && uncnstr(args[2])) { + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + add_def(args[1], r); + add_def(args[2], r); + return r; + } + if (uncnstr(args[0]) && uncnstr(args[1])) { + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + add_def(args[0], m().mk_true()); + add_def(args[1], r); + return r; + } + if (uncnstr(args[0]) && uncnstr(args[2])) { + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + add_def(args[0], m().mk_false()); + add_def(args[2], r); + return r; + } + return nullptr; + case OP_NOT: + SASSERT(num == 1); + if (uncnstr(args[0])) { + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + if (m_mc) + add_def(args[0], m().mk_not(r)); + return r; + } + return nullptr; + case OP_AND: + if (num > 0 && uncnstr(num, args)) { + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + if (m_mc) + add_defs(num, args, r, m().mk_true()); + return r; + } + return nullptr; + case OP_OR: + if (num > 0 && uncnstr(num, args)) { + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + if (m_mc) + add_defs(num, args, r, m().mk_false()); + return r; + } + return nullptr; + case OP_EQ: + SASSERT(num == 2); + return process_eq(f, args[0], args[1]); + default: + return nullptr; + } + } - app * process_add(family_id fid, decl_kind add_k, decl_kind sub_k, unsigned num, expr * const * args) { - if (num == 0) - return nullptr; - unsigned i; - expr * v = nullptr; - for (i = 0; i < num; i++) { - expr * arg = args[i]; - if (uncnstr(arg)) { - v = arg; - break; - } - } - if (v == nullptr) - return nullptr; - app * u; - if (!mk_fresh_uncnstr_var_for(m().mk_app(fid, add_k, num, args), u)) - return u; - if (!m_mc) - return u; - ptr_buffer new_args; - for (unsigned j = 0; j < num; j++) { - if (j == i) - continue; - new_args.push_back(args[j]); - } - if (new_args.empty()) { - add_def(v, u); - } - else { - expr * rest; - if (new_args.size() == 1) - rest = new_args[0]; - else - rest = m().mk_app(fid, add_k, new_args.size(), new_args.c_ptr()); - add_def(v, m().mk_app(fid, sub_k, u, rest)); - } + app * process_le_ge(func_decl * f, expr * arg1, expr * arg2, bool le) { + expr * v; + expr * t; + if (uncnstr(arg1)) { + v = arg1; + t = arg2; + } + else if (uncnstr(arg2)) { + v = arg2; + t = arg1; + le = !le; + } + else { + return nullptr; + } + app * u; + if (!mk_fresh_uncnstr_var_for(f, arg1, arg2, u)) return u; - } - - app * process_arith_mul(func_decl * f, unsigned num, expr * const * args) { - if (num == 0) - return nullptr; - sort * s = m().get_sort(args[0]); - if (uncnstr(num, args)) { - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - if (m_mc) - add_defs(num, args, r, m_a_util.mk_numeral(rational(1), s)); - return r; - } - // c * v case for reals - bool is_int; - rational val; - if (num == 2 && uncnstr(args[1]) && m_a_util.is_numeral(args[0], val, is_int) && !is_int) { - if (val.is_zero()) - return nullptr; - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - if (m_mc) { - val = rational(1) / val; - add_def(args[1], m_a_util.mk_mul(m_a_util.mk_numeral(val, false), r)); - } - return r; - } + if (!m_mc) + return u; + // v = ite(u, t, t + 1) if le + // v = ite(u, t, t - 1) if !le + add_def(v, m().mk_ite(u, t, m_a_util.mk_add(t, m_a_util.mk_numeral(rational(le ? 1 : -1), m().get_sort(arg1))))); + return u; + } + + app * process_add(family_id fid, decl_kind add_k, decl_kind sub_k, unsigned num, expr * const * args) { + if (num == 0) return nullptr; - } - - app * process_arith_app(func_decl * f, unsigned num, expr * const * args) { - - SASSERT(f->get_family_id() == m_a_util.get_family_id()); - switch (f->get_decl_kind()) { - case OP_ADD: - return process_add(f->get_family_id(), OP_ADD, OP_SUB, num, args); - case OP_MUL: - return process_arith_mul(f, num, args); - case OP_LE: - SASSERT(num == 2); - return process_le_ge(f, args[0], args[1], true); - case OP_GE: - SASSERT(num == 2); - return process_le_ge(f, args[0], args[1], false); - default: - return nullptr; + unsigned i; + expr * v = nullptr; + for (i = 0; i < num; i++) { + expr * arg = args[i]; + if (uncnstr(arg)) { + v = arg; + break; } } - - app * process_bv_mul(func_decl * f, unsigned num, expr * const * args) { - if (num == 0) - return nullptr; - if (uncnstr(num, args)) { - sort * s = m().get_sort(args[0]); - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - if (m_mc) - add_defs(num, args, r, m_bv_util.mk_numeral(rational(1), s)); - return r; - } - // c * v (c is even) case - unsigned bv_size; - rational val; - rational inv; - if (num == 2 && - uncnstr(args[1]) && - m_bv_util.is_numeral(args[0], val, bv_size) && - m_bv_util.mult_inverse(val, bv_size, inv)) { - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - sort * s = m().get_sort(args[1]); - if (m_mc) - add_def(args[1], m_bv_util.mk_bv_mul(m_bv_util.mk_numeral(inv, s), r)); - return r; - } + if (v == nullptr) return nullptr; + app * u; + if (!mk_fresh_uncnstr_var_for(m().mk_app(fid, add_k, num, args), u)) + return u; + if (!m_mc) + return u; + ptr_buffer new_args; + for (unsigned j = 0; j < num; j++) { + if (j == i) + continue; + new_args.push_back(args[j]); } - - app * process_extract(func_decl * f, expr * arg) { - if (!uncnstr(arg)) - return nullptr; + if (new_args.empty()) { + add_def(v, u); + } + else { + expr * rest; + if (new_args.size() == 1) + rest = new_args[0]; + else + rest = m().mk_app(fid, add_k, new_args.size(), new_args.c_ptr()); + add_def(v, m().mk_app(fid, sub_k, u, rest)); + } + return u; + } + + app * process_arith_mul(func_decl * f, unsigned num, expr * const * args) { + if (num == 0) + return nullptr; + sort * s = m().get_sort(args[0]); + if (uncnstr(num, args)) { app * r; - if (!mk_fresh_uncnstr_var_for(f, arg, r)) + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; - if (!m_mc) - return r; - unsigned high = m_bv_util.get_extract_high(f); - unsigned low = m_bv_util.get_extract_low(f); - unsigned bv_size = m_bv_util.get_bv_size(m().get_sort(arg)); - if (bv_size == high - low + 1) { - add_def(arg, r); - } - else { - ptr_buffer args; - if (high < bv_size - 1) - args.push_back(m_bv_util.mk_numeral(rational(0), bv_size - high - 1)); - args.push_back(r); - if (low > 0) - args.push_back(m_bv_util.mk_numeral(rational(0), low)); - add_def(arg, m_bv_util.mk_concat(args.size(), args.c_ptr())); - } + if (m_mc) + add_defs(num, args, r, m_a_util.mk_numeral(rational(1), s)); return r; } - - app * process_bv_div(func_decl * f, expr * arg1, expr * arg2) { - if (uncnstr(arg1) && uncnstr(arg2)) { - sort * s = m().get_sort(arg1); - app * r; - if (!mk_fresh_uncnstr_var_for(f, arg1, arg2, r)) - return r; - if (!m_mc) - return r; - add_def(arg1, r); - add_def(arg2, m_bv_util.mk_numeral(rational(1), s)); - return r; - } - return nullptr; - } - - app * process_concat(func_decl * f, unsigned num, expr * const * args) { - if (num == 0) - return nullptr; - if (!uncnstr(num, args)) + // c * v case for reals + bool is_int; + rational val; + if (num == 2 && uncnstr(args[1]) && m_a_util.is_numeral(args[0], val, is_int) && !is_int) { + if (val.is_zero()) return nullptr; app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; if (m_mc) { - unsigned i = num; - unsigned low = 0; - while (i > 0) { - --i; - expr * arg = args[i]; - unsigned sz = m_bv_util.get_bv_size(arg); - add_def(arg, m_bv_util.mk_extract(low + sz - 1, low, r)); - low += sz; - } + val = rational(1) / val; + add_def(args[1], m_a_util.mk_mul(m_a_util.mk_numeral(val, false), r)); } return r; } + return nullptr; + } + + app * process_arith_app(func_decl * f, unsigned num, expr * const * args) { - app * process_bv_le(func_decl * f, expr * arg1, expr * arg2, bool is_signed) { - if (m_produce_proofs) { - // The result of bv_le is not just introducing a new fresh name, - // we need a side condition. - // TODO: the correct proof step - return nullptr; - } - if (uncnstr(arg1)) { - // v <= t - expr * v = arg1; - expr * t = arg2; - // v <= t ---> (u or t == MAX) u is fresh - // add definition v = ite(u or t == MAX, t, t+1) - unsigned bv_sz = m_bv_util.get_bv_size(arg1); - rational MAX; - if (is_signed) - MAX = rational::power_of_two(bv_sz - 1) - rational(1); - else - MAX = rational::power_of_two(bv_sz) - rational(1); - app * u; - bool is_new = mk_fresh_uncnstr_var_for(f, arg1, arg2, u); - app * r = m().mk_or(u, m().mk_eq(t, m_bv_util.mk_numeral(MAX, bv_sz))); - if (m_mc && is_new) - add_def(v, m().mk_ite(r, t, m_bv_util.mk_bv_add(t, m_bv_util.mk_numeral(rational(1), bv_sz)))); - return r; - } - if (uncnstr(arg2)) { - // v >= t - expr * v = arg2; - expr * t = arg1; - // v >= t ---> (u ot t == MIN) u is fresh - // add definition v = ite(u or t == MIN, t, t-1) - unsigned bv_sz = m_bv_util.get_bv_size(arg1); - rational MIN; - if (is_signed) - MIN = -rational::power_of_two(bv_sz - 1); - else - MIN = rational(0); - app * u; - bool is_new = mk_fresh_uncnstr_var_for(f, arg1, arg2, u); - app * r = m().mk_or(u, m().mk_eq(t, m_bv_util.mk_numeral(MIN, bv_sz))); - if (m_mc && is_new) - add_def(v, m().mk_ite(r, t, m_bv_util.mk_bv_sub(t, m_bv_util.mk_numeral(rational(1), bv_sz)))); - return r; - } + SASSERT(f->get_family_id() == m_a_util.get_family_id()); + switch (f->get_decl_kind()) { + case OP_ADD: + return process_add(f->get_family_id(), OP_ADD, OP_SUB, num, args); + case OP_MUL: + return process_arith_mul(f, num, args); + case OP_LE: + SASSERT(num == 2); + return process_le_ge(f, args[0], args[1], true); + case OP_GE: + SASSERT(num == 2); + return process_le_ge(f, args[0], args[1], false); + default: return nullptr; } - - app * process_bv_app(func_decl * f, unsigned num, expr * const * args) { - SASSERT(f->get_family_id() == m_bv_util.get_family_id()); - switch (f->get_decl_kind()) { - case OP_BADD: - return process_add(f->get_family_id(), OP_BADD, OP_BSUB, num, args); - case OP_BMUL: - return process_bv_mul(f, num, args); - case OP_BSDIV: - case OP_BUDIV: - case OP_BSDIV_I: - case OP_BUDIV_I: - SASSERT(num == 2); - return process_bv_div(f, args[0], args[1]); - case OP_SLEQ: - SASSERT(num == 2); - return process_bv_le(f, args[0], args[1], true); - case OP_ULEQ: - SASSERT(num == 2); - return process_bv_le(f, args[0], args[1], false); - case OP_CONCAT: - return process_concat(f, num, args); - case OP_EXTRACT: - SASSERT(num == 1); - return process_extract(f, args[0]); - case OP_BNOT: - SASSERT(num == 1); - if (uncnstr(args[0])) { - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - if (m_mc) - add_def(args[0], m().mk_app(f, r)); - return r; - } - return nullptr; - case OP_BOR: - if (num > 0 && uncnstr(num, args)) { - sort * s = m().get_sort(args[0]); - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - if (m_mc) - add_defs(num, args, r, m_bv_util.mk_numeral(rational(0), s)); - return r; - } - return nullptr; - default: - return nullptr; + } + + app * process_bv_mul(func_decl * f, unsigned num, expr * const * args) { + if (num == 0) + return nullptr; + if (uncnstr(num, args)) { + sort * s = m().get_sort(args[0]); + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + if (m_mc) + add_defs(num, args, r, m_bv_util.mk_numeral(rational(1), s)); + return r; + } + // c * v (c is even) case + unsigned bv_size; + rational val; + rational inv; + if (num == 2 && + uncnstr(args[1]) && + m_bv_util.is_numeral(args[0], val, bv_size) && + m_bv_util.mult_inverse(val, bv_size, inv)) { + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + sort * s = m().get_sort(args[1]); + if (m_mc) + add_def(args[1], m_bv_util.mk_bv_mul(m_bv_util.mk_numeral(inv, s), r)); + return r; + } + return nullptr; + } + + app * process_extract(func_decl * f, expr * arg) { + if (!uncnstr(arg)) + return nullptr; + app * r; + if (!mk_fresh_uncnstr_var_for(f, arg, r)) + return r; + if (!m_mc) + return r; + unsigned high = m_bv_util.get_extract_high(f); + unsigned low = m_bv_util.get_extract_low(f); + unsigned bv_size = m_bv_util.get_bv_size(m().get_sort(arg)); + if (bv_size == high - low + 1) { + add_def(arg, r); + } + else { + ptr_buffer args; + if (high < bv_size - 1) + args.push_back(m_bv_util.mk_numeral(rational(0), bv_size - high - 1)); + args.push_back(r); + if (low > 0) + args.push_back(m_bv_util.mk_numeral(rational(0), low)); + add_def(arg, m_bv_util.mk_concat(args.size(), args.c_ptr())); + } + return r; + } + + app * process_bv_div(func_decl * f, expr * arg1, expr * arg2) { + if (uncnstr(arg1) && uncnstr(arg2)) { + sort * s = m().get_sort(arg1); + app * r; + if (!mk_fresh_uncnstr_var_for(f, arg1, arg2, r)) + return r; + if (!m_mc) + return r; + add_def(arg1, r); + add_def(arg2, m_bv_util.mk_numeral(rational(1), s)); + return r; + } + return nullptr; + } + + app * process_concat(func_decl * f, unsigned num, expr * const * args) { + if (num == 0) + return nullptr; + if (!uncnstr(num, args)) + return nullptr; + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + if (m_mc) { + unsigned i = num; + unsigned low = 0; + while (i > 0) { + --i; + expr * arg = args[i]; + unsigned sz = m_bv_util.get_bv_size(arg); + add_def(arg, m_bv_util.mk_extract(low + sz - 1, low, r)); + low += sz; } } - - app * process_array_app(func_decl * f, unsigned num, expr * const * args) { - SASSERT(f->get_family_id() == m_ar_util.get_family_id()); - switch (f->get_decl_kind()) { - case OP_SELECT: - if (uncnstr(args[0])) { - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - sort * s = m().get_sort(args[0]); - if (m_mc) - add_def(args[0], m_ar_util.mk_const_array(s, r)); - return r; - } - return nullptr; - case OP_STORE: - if (uncnstr(args[0]) && uncnstr(args[num-1])) { - app * r; - if (!mk_fresh_uncnstr_var_for(f, num, args, r)) - return r; - if (m_mc) { - add_def(args[num-1], m().mk_app(m_ar_util.get_family_id(), OP_SELECT, num-1, args)); - add_def(args[0], r); - } - return r; - } - default: - return nullptr; - } - } - - app * process_datatype_app(func_decl * f, unsigned num, expr * const * args) { - if (m_dt_util.is_recognizer(f)) { - SASSERT(num == 1); - if (uncnstr(args[0])) { - if (!m_mc) { - app * r; - mk_fresh_uncnstr_var_for(f, num, args, r); - return r; - } - // TODO: handle model generation - } - } - else if (m_dt_util.is_accessor(f)) { - SASSERT(num == 1); - if (uncnstr(args[0])) { - if (!m_mc) { - app * r; - mk_fresh_uncnstr_var_for(f, num, args, r); - return r; - } - func_decl * c = m_dt_util.get_accessor_constructor(f); - for (unsigned i = 0; i < c->get_arity(); i++) - if (!m().is_fully_interp(c->get_domain(i))) - return nullptr; - app * u; - if (!mk_fresh_uncnstr_var_for(f, num, args, u)) - return u; - ptr_vector const & accs = *m_dt_util.get_constructor_accessors(c); - ptr_buffer new_args; - for (unsigned i = 0; i < accs.size(); i++) { - if (accs[i] == f) - new_args.push_back(u); - else - new_args.push_back(m().get_some_value(c->get_domain(i))); - } - add_def(args[0], m().mk_app(c, new_args.size(), new_args.c_ptr())); - return u; - } - } - else if (m_dt_util.is_constructor(f)) { - if (uncnstr(num, args)) { - app * u; - if (!mk_fresh_uncnstr_var_for(f, num, args, u)) - return u; - if (!m_mc) - return u; - ptr_vector const & accs = *m_dt_util.get_constructor_accessors(f); - for (unsigned i = 0; i < num; i++) { - add_def(args[i], m().mk_app(accs[i], u)); - } - return u; - } - } + return r; + } + + app * process_bv_le(func_decl * f, expr * arg1, expr * arg2, bool is_signed) { + if (m_produce_proofs) { + // The result of bv_le is not just introducing a new fresh name, + // we need a side condition. + // TODO: the correct proof step return nullptr; } - - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - if (num == 0) - return BR_FAILED; - family_id fid = f->get_family_id(); - if (fid == null_family_id) - return BR_FAILED; - - for (unsigned i = 0; i < num; i++) { - if (!is_ground(args[i])) - return BR_FAILED; // non-ground terms are not handled. - } - - app * u = nullptr; - - if (fid == m().get_basic_family_id()) - u = process_basic_app(f, num, args); - else if (fid == m_a_util.get_family_id()) - u = process_arith_app(f, num, args); - else if (fid == m_bv_util.get_family_id()) - u = process_bv_app(f, num, args); - else if (fid == m_ar_util.get_family_id()) - u = process_array_app(f, num, args); - else if (fid == m_dt_util.get_family_id()) - u = process_datatype_app(f, num, args); - - if (u == nullptr) - return BR_FAILED; - - result = u; - if (m_produce_proofs) { - expr * s = m().mk_app(f, num, args); - expr * eq = m().mk_eq(s, u); - proof * pr1 = m().mk_def_intro(eq); - result_pr = m().mk_apply_def(s, u, pr1); - } - - return BR_DONE; - } - }; - - class rw : public rewriter_tpl { - rw_cfg m_cfg; - public: - rw(ast_manager & m, bool produce_proofs, obj_hashtable & vars, mc * _m, - unsigned long long max_memory, unsigned max_steps): - rewriter_tpl(m, produce_proofs, m_cfg), - m_cfg(m, produce_proofs, vars, _m, max_memory, max_steps) { - } - }; - - ast_manager & m_manager; - ref m_mc; - obj_hashtable m_vars; - scoped_ptr m_rw; - unsigned m_num_elim_apps; - unsigned long long m_max_memory; - unsigned m_max_steps; - - imp(ast_manager & m, params_ref const & p): - m_manager(m), - m_num_elim_apps(0) { - updt_params(p); - } - - void updt_params(params_ref const & p) { - m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); - m_max_steps = p.get_uint("max_steps", UINT_MAX); - } - - ast_manager & m() { return m_manager; } - - void init_mc(bool produce_models) { - m_mc = nullptr; - if (produce_models) { - m_mc = alloc(mc, m(), "elim_uncstr"); - } - } - - void init_rw(bool produce_proofs) { - m_rw = alloc(rw, m(), produce_proofs, m_vars, m_mc.get(), m_max_memory, m_max_steps); - } - - void operator()(goal_ref const & g, goal_ref_buffer & result) { - bool produce_proofs = g->proofs_enabled(); - - TRACE("elim_uncnstr_bug", g->display(tout);); - tactic_report report("elim-uncnstr-vars", *g); - m_vars.reset(); - collect_occs p; - p(*g, m_vars); - if (m_vars.empty()) { - result.push_back(g.get()); - // did not increase depth since it didn't do anything. - return; - } - bool modified = true; - TRACE("elim_uncnstr", tout << "unconstrained variables...\n"; - for (expr * v : m_vars) tout << mk_ismt2_pp(v, m()) << " "; - tout << "\n";); - init_mc(g->models_enabled()); - init_rw(produce_proofs); - - expr_ref new_f(m()); - proof_ref new_pr(m()); - unsigned round = 0; - unsigned size = g->size(); - unsigned idx = 0; - while (true) { - for (; idx < size; idx++) { - expr * f = g->form(idx); - m_rw->operator()(f, new_f, new_pr); - if (f == new_f) - continue; - modified = true; - if (produce_proofs) { - proof * pr = g->pr(idx); - new_pr = m().mk_modus_ponens(pr, new_pr); - } - g->update(idx, new_f, new_pr, g->dep(idx)); - } - if (!modified) { - if (round == 0) { - } - else { - m_num_elim_apps = m_rw->cfg().m_fresh_vars.size(); - g->add(m_mc.get()); - } - TRACE("elim_uncnstr", if (m_mc) m_mc->display(tout); else tout << "no mc\n";); - m_mc = nullptr; - m_rw = nullptr; - result.push_back(g.get()); - g->inc_depth(); - return; - } - modified = false; - round ++; - size = g->size(); - m_rw->reset(); // reset cache - m_vars.reset(); - { - collect_occs p; - p(*g, m_vars); - } - if (m_vars.empty()) - idx = size; // force to finish + if (uncnstr(arg1)) { + // v <= t + expr * v = arg1; + expr * t = arg2; + // v <= t ---> (u or t == MAX) u is fresh + // add definition v = ite(u or t == MAX, t, t+1) + unsigned bv_sz = m_bv_util.get_bv_size(arg1); + rational MAX; + if (is_signed) + MAX = rational::power_of_two(bv_sz - 1) - rational(1); else - idx = 0; + MAX = rational::power_of_two(bv_sz) - rational(1); + app * u; + bool is_new = mk_fresh_uncnstr_var_for(f, arg1, arg2, u); + app * r = m().mk_or(u, m().mk_eq(t, m_bv_util.mk_numeral(MAX, bv_sz))); + if (m_mc && is_new) + add_def(v, m().mk_ite(r, t, m_bv_util.mk_bv_add(t, m_bv_util.mk_numeral(rational(1), bv_sz)))); + return r; + } + if (uncnstr(arg2)) { + // v >= t + expr * v = arg2; + expr * t = arg1; + // v >= t ---> (u ot t == MIN) u is fresh + // add definition v = ite(u or t == MIN, t, t-1) + unsigned bv_sz = m_bv_util.get_bv_size(arg1); + rational MIN; + if (is_signed) + MIN = -rational::power_of_two(bv_sz - 1); + else + MIN = rational(0); + app * u; + bool is_new = mk_fresh_uncnstr_var_for(f, arg1, arg2, u); + app * r = m().mk_or(u, m().mk_eq(t, m_bv_util.mk_numeral(MIN, bv_sz))); + if (m_mc && is_new) + add_def(v, m().mk_ite(r, t, m_bv_util.mk_bv_sub(t, m_bv_util.mk_numeral(rational(1), bv_sz)))); + return r; + } + return nullptr; + } + + app * process_bv_app(func_decl * f, unsigned num, expr * const * args) { + SASSERT(f->get_family_id() == m_bv_util.get_family_id()); + switch (f->get_decl_kind()) { + case OP_BADD: + return process_add(f->get_family_id(), OP_BADD, OP_BSUB, num, args); + case OP_BMUL: + return process_bv_mul(f, num, args); + case OP_BSDIV: + case OP_BUDIV: + case OP_BSDIV_I: + case OP_BUDIV_I: + SASSERT(num == 2); + return process_bv_div(f, args[0], args[1]); + case OP_SLEQ: + SASSERT(num == 2); + return process_bv_le(f, args[0], args[1], true); + case OP_ULEQ: + SASSERT(num == 2); + return process_bv_le(f, args[0], args[1], false); + case OP_CONCAT: + return process_concat(f, num, args); + case OP_EXTRACT: + SASSERT(num == 1); + return process_extract(f, args[0]); + case OP_BNOT: + SASSERT(num == 1); + if (uncnstr(args[0])) { + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + if (m_mc) + add_def(args[0], m().mk_app(f, r)); + return r; + } + return nullptr; + case OP_BOR: + if (num > 0 && uncnstr(num, args)) { + sort * s = m().get_sort(args[0]); + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + if (m_mc) + add_defs(num, args, r, m_bv_util.mk_numeral(rational(0), s)); + return r; + } + return nullptr; + default: + return nullptr; } } + + app * process_array_app(func_decl * f, unsigned num, expr * const * args) { + SASSERT(f->get_family_id() == m_ar_util.get_family_id()); + switch (f->get_decl_kind()) { + case OP_SELECT: + if (uncnstr(args[0])) { + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + sort * s = m().get_sort(args[0]); + if (m_mc) + add_def(args[0], m_ar_util.mk_const_array(s, r)); + return r; + } + return nullptr; + case OP_STORE: + if (uncnstr(args[0]) && uncnstr(args[num-1])) { + app * r; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + if (m_mc) { + add_def(args[num-1], m().mk_app(m_ar_util.get_family_id(), OP_SELECT, num-1, args)); + add_def(args[0], r); + } + return r; + } + default: + return nullptr; + } + } + + app * process_datatype_app(func_decl * f, unsigned num, expr * const * args) { + if (m_dt_util.is_recognizer(f)) { + SASSERT(num == 1); + if (uncnstr(args[0])) { + if (!m_mc) { + app * r; + mk_fresh_uncnstr_var_for(f, num, args, r); + return r; + } + // TODO: handle model generation + } + } + else if (m_dt_util.is_accessor(f)) { + SASSERT(num == 1); + if (uncnstr(args[0])) { + if (!m_mc) { + app * r; + mk_fresh_uncnstr_var_for(f, num, args, r); + return r; + } + func_decl * c = m_dt_util.get_accessor_constructor(f); + for (unsigned i = 0; i < c->get_arity(); i++) + if (!m().is_fully_interp(c->get_domain(i))) + return nullptr; + app * u; + if (!mk_fresh_uncnstr_var_for(f, num, args, u)) + return u; + ptr_vector const & accs = *m_dt_util.get_constructor_accessors(c); + ptr_buffer new_args; + for (unsigned i = 0; i < accs.size(); i++) { + if (accs[i] == f) + new_args.push_back(u); + else + new_args.push_back(m().get_some_value(c->get_domain(i))); + } + add_def(args[0], m().mk_app(c, new_args.size(), new_args.c_ptr())); + return u; + } + } + else if (m_dt_util.is_constructor(f)) { + if (uncnstr(num, args)) { + app * u; + if (!mk_fresh_uncnstr_var_for(f, num, args, u)) + return u; + if (!m_mc) + return u; + ptr_vector const & accs = *m_dt_util.get_constructor_accessors(f); + for (unsigned i = 0; i < num; i++) { + add_def(args[i], m().mk_app(accs[i], u)); + } + return u; + } + } + return nullptr; + } + + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + if (num == 0) + return BR_FAILED; + family_id fid = f->get_family_id(); + if (fid == null_family_id) + return BR_FAILED; + for (unsigned i = 0; i < num; i++) { + if (!is_ground(args[i])) + return BR_FAILED; // non-ground terms are not handled. + } + + app * u = nullptr; + + if (fid == m().get_basic_family_id()) + u = process_basic_app(f, num, args); + else if (fid == m_a_util.get_family_id()) + u = process_arith_app(f, num, args); + else if (fid == m_bv_util.get_family_id()) + u = process_bv_app(f, num, args); + else if (fid == m_ar_util.get_family_id()) + u = process_array_app(f, num, args); + else if (fid == m_dt_util.get_family_id()) + u = process_datatype_app(f, num, args); + + if (u == nullptr) + return BR_FAILED; + + result = u; + if (m_produce_proofs) { + expr * s = m().mk_app(f, num, args); + expr * eq = m().mk_eq(s, u); + proof * pr1 = m().mk_def_intro(eq); + result_pr = m().mk_apply_def(s, u, pr1); + } + + return BR_DONE; + } }; - imp * m_imp; + class rw : public rewriter_tpl { + rw_cfg m_cfg; + public: + rw(ast_manager & m, bool produce_proofs, obj_hashtable & vars, mc * _m, + unsigned long long max_memory, unsigned max_steps): + rewriter_tpl(m, produce_proofs, m_cfg), + m_cfg(m, produce_proofs, vars, _m, max_memory, max_steps) { + } + }; + + ast_manager & m_manager; + ref m_mc; + obj_hashtable m_vars; + scoped_ptr m_rw; + unsigned m_num_elim_apps = 0; + unsigned long long m_max_memory; + unsigned m_max_steps; + + ast_manager & m() { return m_manager; } + + void init_mc(bool produce_models) { + m_mc = nullptr; + if (produce_models) { + m_mc = alloc(mc, m(), "elim_uncstr"); + } + } + + void init_rw(bool produce_proofs) { + m_rw = alloc(rw, m(), produce_proofs, m_vars, m_mc.get(), m_max_memory, m_max_steps); + } + + void run(goal_ref const & g, goal_ref_buffer & result) { + bool produce_proofs = g->proofs_enabled(); + + TRACE("elim_uncnstr_bug", g->display(tout);); + tactic_report report("elim-uncnstr-vars", *g); + m_vars.reset(); + collect_occs p; + p(*g, m_vars); + if (m_vars.empty()) { + result.push_back(g.get()); + // did not increase depth since it didn't do anything. + return; + } + bool modified = true; + TRACE("elim_uncnstr", tout << "unconstrained variables...\n"; + for (expr * v : m_vars) tout << mk_ismt2_pp(v, m()) << " "; + tout << "\n";); + init_mc(g->models_enabled()); + init_rw(produce_proofs); + + expr_ref new_f(m()); + proof_ref new_pr(m()); + unsigned round = 0; + unsigned size = g->size(); + unsigned idx = 0; + while (true) { + for (; idx < size; idx++) { + expr * f = g->form(idx); + m_rw->operator()(f, new_f, new_pr); + if (f == new_f) + continue; + modified = true; + if (produce_proofs) { + proof * pr = g->pr(idx); + new_pr = m().mk_modus_ponens(pr, new_pr); + } + g->update(idx, new_f, new_pr, g->dep(idx)); + } + if (!modified) { + if (round == 0) { + } + else { + m_num_elim_apps = m_rw->cfg().m_fresh_vars.size(); + g->add(m_mc.get()); + } + TRACE("elim_uncnstr", if (m_mc) m_mc->display(tout); else tout << "no mc\n";); + m_mc = nullptr; + m_rw = nullptr; + result.push_back(g.get()); + g->inc_depth(); + return; + } + modified = false; + round ++; + size = g->size(); + m_rw->reset(); // reset cache + m_vars.reset(); + { + collect_occs p; + p(*g, m_vars); + } + if (m_vars.empty()) + idx = size; // force to finish + else + idx = 0; + } + } + params_ref m_params; public: elim_uncnstr_tactic(ast_manager & m, params_ref const & p): - m_params(p) { - m_imp = alloc(imp, m, p); + m_manager(m), m_params(p) { + updt_params(p); } tactic * translate(ast_manager & m) override { return alloc(elim_uncnstr_tactic, m, m_params); } - - ~elim_uncnstr_tactic() override { - dealloc(m_imp); - } void updt_params(params_ref const & p) override { m_params = p; - m_imp->updt_params(p); + m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_max_steps = p.get_uint("max_steps", UINT_MAX); } void collect_param_descrs(param_descrs & r) override { @@ -912,32 +895,26 @@ public: void operator()(goal_ref const & g, goal_ref_buffer & result) override { - (*m_imp)(g, result); - report_tactic_progress(":num-elim-apps", get_num_elim_apps()); + run(g, result); + report_tactic_progress(":num-elim-apps", m_num_elim_apps); } void cleanup() override { - unsigned num_elim_apps = get_num_elim_apps(); - ast_manager & m = m_imp->m_manager; - imp * d = alloc(imp, m, m_params); - std::swap(d, m_imp); - dealloc(d); - m_imp->m_num_elim_apps = num_elim_apps; - } - - unsigned get_num_elim_apps() const { - return m_imp->m_num_elim_apps; + m_mc = nullptr; + m_rw = nullptr; + m_vars.reset(); } void collect_statistics(statistics & st) const override { - st.update("eliminated applications", get_num_elim_apps()); + st.update("eliminated applications", m_num_elim_apps); } void reset_statistics() override { - m_imp->m_num_elim_apps = 0; + m_num_elim_apps = 0; } }; +} tactic * mk_elim_uncnstr_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(elim_uncnstr_tactic, m, p)); diff --git a/src/tactic/core/elim_uncnstr_tactic.h b/src/tactic/core/elim_uncnstr_tactic.h index 19f9021ac..5824a6974 100644 --- a/src/tactic/core/elim_uncnstr_tactic.h +++ b/src/tactic/core/elim_uncnstr_tactic.h @@ -16,8 +16,7 @@ Author: Notes: --*/ -#ifndef ELIM_UNCNSTR_TACTIC_H_ -#define ELIM_UNCNSTR_TACTIC_H_ +#pragma once #include "util/params.h" @@ -29,5 +28,3 @@ tactic * mk_elim_uncnstr_tactic(ast_manager & m, params_ref const & p = params_r /* ADD_TACTIC("elim-uncnstr", "eliminate application containing unconstrained variables.", "mk_elim_uncnstr_tactic(m, p)") */ -#endif - diff --git a/src/tactic/core/propagate_values_tactic.cpp b/src/tactic/core/propagate_values_tactic.cpp index 41e48d864..351b606e6 100644 --- a/src/tactic/core/propagate_values_tactic.cpp +++ b/src/tactic/core/propagate_values_tactic.cpp @@ -23,236 +23,206 @@ Revision History: #include "ast/ast_smt2_pp.h" #include "ast/expr_substitution.h" #include "tactic/goal_shared_occs.h" -#include "ast/pb_decl_plugin.h" +#include "tactic/tactic_params.hpp" +namespace { class propagate_values_tactic : public tactic { - struct imp { - ast_manager & m; - th_rewriter m_r; - scoped_ptr m_subst; - goal * m_goal; - goal_shared_occs m_occs; - unsigned m_idx; - unsigned m_max_rounds; - bool m_modified; - - imp(ast_manager & m, params_ref const & p): - m(m), - m_r(m, p), - m_goal(nullptr), - m_occs(m, true /* track atoms */) { - updt_params_core(p); - } + ast_manager & m; + th_rewriter m_r; + scoped_ptr m_subst; + goal * m_goal; + goal_shared_occs m_occs; + unsigned m_idx; + unsigned m_max_rounds; + bool m_modified; + params_ref m_params; - ~imp() { } + void updt_params_core(params_ref const & p) { + tactic_params tp(p); + m_max_rounds = p.get_uint("max_rounds", tp.propagate_values_max_rounds()); + } - void updt_params_core(params_ref const & p) { - m_max_rounds = p.get_uint("max_rounds", 4); - } - - void updt_params(params_ref const & p) { - m_r.updt_params(p); - updt_params_core(p); - } - - - bool is_shared(expr * t) { - return m_occs.is_shared(t); - } - - bool is_shared_neg(expr * t, expr * & atom) { - if (!m.is_not(t, atom)) - return false; - return is_shared(atom); - } + bool is_shared(expr * t) { + return m_occs.is_shared(t); + } - bool is_shared_eq(expr * t, expr * & lhs, expr * & value) { - expr* arg1, *arg2; - if (!m.is_eq(t, arg1, arg2)) - return false; - if (m.is_value(arg1) && is_shared(arg2)) { - lhs = arg2; - value = arg1; - return true; - } - if (m.is_value(arg2) && is_shared(arg1)) { - lhs = arg1; - value = arg2; - return true; - } + bool is_shared_neg(expr * t, expr * & atom) { + if (!m.is_not(t, atom)) return false; + return is_shared(atom); + } + + bool is_shared_eq(expr * t, expr * & lhs, expr * & value) { + expr* arg1, *arg2; + if (!m.is_eq(t, arg1, arg2)) + return false; + if (m.is_value(arg1) && is_shared(arg2)) { + lhs = arg2; + value = arg1; + return true; + } + if (m.is_value(arg2) && is_shared(arg1)) { + lhs = arg1; + value = arg2; + return true; + } + return false; + } + + void push_result(expr * new_curr, proof * new_pr) { + if (m_goal->proofs_enabled()) { + proof * pr = m_goal->pr(m_idx); + new_pr = m.mk_modus_ponens(pr, new_pr); } - void push_result(expr * new_curr, proof * new_pr) { - if (m_goal->proofs_enabled()) { - proof * pr = m_goal->pr(m_idx); - new_pr = m.mk_modus_ponens(pr, new_pr); + expr_dependency_ref new_d(m); + if (m_goal->unsat_core_enabled()) { + new_d = m_goal->dep(m_idx); + expr_dependency * used_d = m_r.get_used_dependencies(); + if (used_d != nullptr) { + new_d = m.mk_join(new_d, used_d); + m_r.reset_used_dependencies(); } - - expr_dependency_ref new_d(m); - if (m_goal->unsat_core_enabled()) { - new_d = m_goal->dep(m_idx); - expr_dependency * used_d = m_r.get_used_dependencies(); - if (used_d != nullptr) { - new_d = m.mk_join(new_d, used_d); - m_r.reset_used_dependencies(); + } + + m_goal->update(m_idx, new_curr, new_pr, new_d); + + if (is_shared(new_curr)) { + m_subst->insert(new_curr, m.mk_true(), m.mk_iff_true(new_pr), new_d); + } + expr * atom; + if (is_shared_neg(new_curr, atom)) { + m_subst->insert(atom, m.mk_false(), m.mk_iff_false(new_pr), new_d); + } + expr * lhs, * value; + if (is_shared_eq(new_curr, lhs, value)) { + TRACE("shallow_context_simplifier_bug", tout << "found eq:\n" << mk_ismt2_pp(new_curr, m) << "\n";); + m_subst->insert(lhs, value, new_pr, new_d); + } + } + + void process_current() { + expr * curr = m_goal->form(m_idx); + expr_ref new_curr(m); + proof_ref new_pr(m); + + if (!m_subst->empty()) { + m_r(curr, new_curr, new_pr); + } + else { + new_curr = curr; + if (m.proofs_enabled()) + new_pr = m.mk_reflexivity(curr); + } + + TRACE("shallow_context_simplifier_bug", tout << mk_ismt2_pp(curr, m) << "\n---->\n" << mk_ismt2_pp(new_curr, m) << "\n";); + if (new_curr != curr) { + m_modified = true; + } + push_result(new_curr, new_pr); + } + + void run(goal_ref const & g, goal_ref_buffer & result) { + SASSERT(g->is_well_sorted()); + tactic_report report("propagate-values", *g); + m_goal = g.get(); + + bool forward = true; + expr_ref new_curr(m); + proof_ref new_pr(m); + unsigned size = m_goal->size(); + m_idx = 0; + m_modified = false; + unsigned round = 0; + + + if (m_goal->inconsistent()) + goto end; + + if (m_max_rounds == 0) + goto end; + + m_subst = alloc(expr_substitution, m, g->unsat_core_enabled(), g->proofs_enabled()); + m_r.set_substitution(m_subst.get()); + m_occs(*m_goal); + + while (true) { + TRACE("propagate_values", tout << "while(true) loop\n"; m_goal->display_with_dependencies(tout);); + if (forward) { + for (; m_idx < size; m_idx++) { + process_current(); + if (m_goal->inconsistent()) + goto end; } - } - - m_goal->update(m_idx, new_curr, new_pr, new_d); - - if (is_shared(new_curr)) { - m_subst->insert(new_curr, m.mk_true(), m.mk_iff_true(new_pr), new_d); - } - expr * atom; - if (is_shared_neg(new_curr, atom)) { - m_subst->insert(atom, m.mk_false(), m.mk_iff_false(new_pr), new_d); - } - expr * lhs, * value; - if (is_shared_eq(new_curr, lhs, value)) { - TRACE("shallow_context_simplifier_bug", tout << "found eq:\n" << mk_ismt2_pp(new_curr, m) << "\n";); - m_subst->insert(lhs, value, new_pr, new_d); - } - } - - void process_current() { - expr * curr = m_goal->form(m_idx); - expr_ref new_curr(m); - proof_ref new_pr(m); - - if (!m_subst->empty()) { - m_r(curr, new_curr, new_pr); + if (m_subst->empty() && !m_modified) + goto end; + m_occs(*m_goal); + m_idx = m_goal->size(); + forward = false; + m_subst->reset(); + m_r.set_substitution(m_subst.get()); // reset, but keep substitution } else { - new_curr = curr; - if (m.proofs_enabled()) - new_pr = m.mk_reflexivity(curr); - } - - TRACE("shallow_context_simplifier_bug", tout << mk_ismt2_pp(curr, m) << "\n---->\n" << mk_ismt2_pp(new_curr, m) << "\n";); - if (new_curr != curr) { - m_modified = true; - //if (has_pb(curr)) - // IF_VERBOSE(0, verbose_stream() << mk_ismt2_pp(curr, m) << "\n---->\n" << mk_ismt2_pp(new_curr, m) << "\n"); - } - push_result(new_curr, new_pr); - } - - bool has_pb(expr* e) { - pb_util pb(m); - if (pb.is_ge(e)) return true; - if (m.is_or(e)) { - for (expr* a : *to_app(e)) { - if (pb.is_ge(a)) return true; - } - } - return false; - } - - void operator()(goal_ref const & g, - goal_ref_buffer & result) { - SASSERT(g->is_well_sorted()); - tactic_report report("propagate-values", *g); - m_goal = g.get(); - - bool forward = true; - expr_ref new_curr(m); - proof_ref new_pr(m); - unsigned size = m_goal->size(); - m_idx = 0; - m_modified = false; - unsigned round = 0; - - - if (m_goal->inconsistent()) - goto end; - - if (m_max_rounds == 0) - goto end; - - m_subst = alloc(expr_substitution, m, g->unsat_core_enabled(), g->proofs_enabled()); - m_r.set_substitution(m_subst.get()); - m_occs(*m_goal); - - while (true) { - TRACE("propagate_values", tout << "while(true) loop\n"; m_goal->display_with_dependencies(tout);); - if (forward) { - for (; m_idx < size; m_idx++) { - process_current(); - if (m_goal->inconsistent()) - goto end; - } - if (m_subst->empty() && !m_modified) + while (m_idx > 0) { + m_idx--; + process_current(); + if (m_goal->inconsistent()) goto end; - m_occs(*m_goal); - m_idx = m_goal->size(); - forward = false; - m_subst->reset(); - m_r.set_substitution(m_subst.get()); // reset, but keep substitution } - else { - while (m_idx > 0) { - m_idx--; - process_current(); - if (m_goal->inconsistent()) - goto end; - } - if (!m_modified) - goto end; - m_subst->reset(); - m_r.set_substitution(m_subst.get()); // reset, but keep substitution - m_modified = false; - m_occs(*m_goal); - m_idx = 0; - size = m_goal->size(); - forward = true; - } - round++; - if (round >= m_max_rounds) - break; - IF_VERBOSE(100, verbose_stream() << "starting new round, goal size: " << m_goal->num_exprs() << std::endl;); - TRACE("propagate_values", tout << "round finished\n"; m_goal->display(tout); tout << "\n";); + if (!m_modified) + goto end; + m_subst->reset(); + m_r.set_substitution(m_subst.get()); // reset, but keep substitution + m_modified = false; + m_occs(*m_goal); + m_idx = 0; + size = m_goal->size(); + forward = true; } - end: - m_goal->elim_redundancies(); - m_goal->inc_depth(); - result.push_back(m_goal); - SASSERT(m_goal->is_well_sorted()); - TRACE("propagate_values", tout << "end\n"; m_goal->display(tout);); - TRACE("propagate_values_core", m_goal->display_with_dependencies(tout);); - m_goal = nullptr; + round++; + if (round >= m_max_rounds) + break; + IF_VERBOSE(100, verbose_stream() << "starting new round, goal size: " << m_goal->num_exprs() << std::endl;); + TRACE("propagate_values", tout << "round finished\n"; m_goal->display(tout); tout << "\n";); } - }; + end: + m_goal->elim_redundancies(); + m_goal->inc_depth(); + result.push_back(m_goal); + SASSERT(m_goal->is_well_sorted()); + TRACE("propagate_values", tout << "end\n"; m_goal->display(tout);); + TRACE("propagate_values_core", m_goal->display_with_dependencies(tout);); + m_goal = nullptr; + } - imp * m_imp; - params_ref m_params; public: propagate_values_tactic(ast_manager & m, params_ref const & p): + m(m), + m_r(m, p), + m_goal(nullptr), + m_occs(m, true /* track atoms */), m_params(p) { - m_imp = alloc(imp, m, p); + updt_params_core(p); } tactic * translate(ast_manager & m) override { return alloc(propagate_values_tactic, m, m_params); } - - ~propagate_values_tactic() override { - dealloc(m_imp); - } void updt_params(params_ref const & p) override { m_params = p; - m_imp->updt_params(p); + m_r.updt_params(p); + updt_params_core(p); } void collect_param_descrs(param_descrs & r) override { th_rewriter::get_param_descrs(r); - r.insert("max_rounds", CPK_UINT, "(default: 2) maximum number of rounds."); + r.insert("max_rounds", CPK_UINT, "(default: 4) maximum number of rounds."); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { - (*m_imp)(in, result); + run(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); @@ -260,15 +230,14 @@ public: } void cleanup() override { - ast_manager & m = m_imp->m; - params_ref p = std::move(m_params); - m_imp->~imp(); - new (m_imp) imp(m, p); + m_r.cleanup(); + m_subst = nullptr; + m_occs.cleanup(); } }; +} tactic * mk_propagate_values_tactic(ast_manager & m, params_ref const & p) { return alloc(propagate_values_tactic, m, p); } - diff --git a/src/tactic/core/propagate_values_tactic.h b/src/tactic/core/propagate_values_tactic.h index 635b0a36f..d9324ff82 100644 --- a/src/tactic/core/propagate_values_tactic.h +++ b/src/tactic/core/propagate_values_tactic.h @@ -17,8 +17,7 @@ Author: Revision History: --*/ -#ifndef PROPAGATE_VALUES_TACTIC_H_ -#define PROPAGATE_VALUES_TACTIC_H_ +#pragma once #include "util/params.h" class ast_manager; @@ -29,5 +28,3 @@ tactic * mk_propagate_values_tactic(ast_manager & m, params_ref const & p = para /* ADD_TACTIC("propagate-values", "propagate constants.", "mk_propagate_values_tactic(m, p)") */ - -#endif diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index f665fe509..1fc0c1f41 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -16,14 +16,19 @@ Author: Revision History: --*/ -#include "tactic/tactical.h" #include "ast/rewriter/expr_replacer.h" -#include "tactic/generic_model_converter.h" -#include "ast/occurs.h" #include "util/cooperate.h" -#include "tactic/goal_shared_occs.h" +#include "ast/occurs.h" +#include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/pb_decl_plugin.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/rewriter/rewriter_def.h" +#include "ast/rewriter/hoist_rewriter.h" +#include "tactic/goal_shared_occs.h" +#include "tactic/tactical.h" +#include "tactic/generic_model_converter.h" +#include "tactic/tactic_params.hpp" class solve_eqs_tactic : public tactic { struct imp { @@ -39,6 +44,7 @@ class solve_eqs_tactic : public tactic { bool m_theory_solver; bool m_ite_solver; unsigned m_max_occs; + bool m_context_solve; scoped_ptr m_subst; scoped_ptr m_norm_subst; expr_sparse_mark m_candidate_vars; @@ -71,9 +77,11 @@ class solve_eqs_tactic : public tactic { ast_manager & m() const { return m_manager; } void updt_params(params_ref const & p) { - m_ite_solver = p.get_bool("ite_solver", true); - m_theory_solver = p.get_bool("theory_solver", true); - m_max_occs = p.get_uint("solve_eqs_max_occs", UINT_MAX); + tactic_params tp(p); + m_ite_solver = p.get_bool("ite_solver", tp.solve_eqs_ite_solver()); + m_theory_solver = p.get_bool("theory_solver", tp.solve_eqs_theory_solver()); + m_max_occs = p.get_uint("solve_eqs_max_occs", tp.solve_eqs_max_occs()); + m_context_solve = p.get_bool("context_solve", tp.solve_eqs_context_solve()); } void checkpoint() { @@ -384,6 +392,20 @@ class solve_eqs_tactic : public tactic { return false; } + + void insert_solution(goal const& g, unsigned idx, expr* f, app* var, expr* def, proof* pr) { + m_vars.push_back(var); + m_candidates.push_back(f); + m_candidate_set.mark(f); + m_candidate_vars.mark(var); + if (m_produce_proofs) { + if (!pr) + pr = g.pr(idx); + else + pr = m().mk_modus_ponens(g.pr(idx), pr); + } + m_subst->insert(var, def, pr, g.dep(idx)); + } /** \brief Start collecting candidates @@ -408,17 +430,7 @@ class solve_eqs_tactic : public tactic { checkpoint(); expr * f = g.form(idx); if (solve(f, var, def, pr)) { - m_vars.push_back(var); - m_candidates.push_back(f); - m_candidate_set.mark(f); - m_candidate_vars.mark(var); - if (m_produce_proofs) { - if (pr == 0) - pr = g.pr(idx); - else - pr = m().mk_modus_ponens(g.pr(idx), pr); - } - m_subst->insert(var, def, pr, g.dep(idx)); + insert_solution(g, idx, f, var, def, pr); } m_num_steps++; } @@ -430,6 +442,172 @@ class solve_eqs_tactic : public tactic { } tout << "\n";); } + + struct nnf_context { + bool m_is_and; + expr_ref_vector m_args; + unsigned m_index; + nnf_context(bool is_and, expr_ref_vector const& args, unsigned idx): + m_is_and(is_and), + m_args(args), + m_index(idx) + {} + }; + + bool is_compatible(goal const& g, unsigned idx, vector const & path, expr* v, expr* eq) { + return is_goal_compatible(g, idx, v, eq) && is_path_compatible(path, v, eq); + } + + bool is_goal_compatible(goal const& g, unsigned idx, expr* v, expr* eq) { + bool all_e = false; + for (unsigned j = 0; j < g.size(); ++j) { + if (j != idx && !check_eq_compat(g.form(j), v, eq, all_e)) { + TRACE("solve_eqs", tout << "occurs goal " << mk_pp(eq, m()) << "\n";); + return false; + } + } + return true; + } + + // + // all_e := all disjunctions contain eq + // + // or, all_e -> skip if all disjunctions contain eq + // or, all_e -> fail if some disjunction contains v but not eq + // or, all_e -> all_e := false if some disjunction does not contain v + // and, all_e -> all_e + // + + bool is_path_compatible(vector const & path, expr* v, expr* eq) { + bool all_e = true; + for (unsigned i = path.size(); i-- > 0; ) { + auto const& p = path[i]; + auto const& args = p.m_args; + if (p.m_is_and && !all_e) { + for (unsigned j = 0; j < args.size(); ++j) { + if (j != p.m_index && occurs(v, args[j])) { + TRACE("solve_eqs", tout << "occurs and " << mk_pp(eq, m()) << " " << mk_pp(args[j], m()) << "\n";); + return false; + } + } + } + else if (!p.m_is_and) { + for (unsigned j = 0; j < args.size(); ++j) { + if (j != p.m_index) { + if (occurs(v, args[j])) { + if (!check_eq_compat(args[j], v, eq, all_e)) { + TRACE("solve_eqs", tout << "occurs or " << mk_pp(eq, m()) << " " << mk_pp(args[j], m()) << "\n";); + return false; + } + } + else { + all_e = false; + } + } + } + } + } + return true; + } + + bool check_eq_compat(expr* f, expr* v, expr* eq, bool& all) { + expr_ref_vector args(m()); + expr* f1 = nullptr; + if (!occurs(v, f)) { + all = false; + return true; + } + if (m().is_not(f, f1) && m().is_or(f1)) { + flatten_and(f, args); + for (expr* arg : args) { + if (arg == eq) { + return true; + } + } + } + else if (m().is_or(f)) { + flatten_or(f, args); + } + else { + return false; + } + + for (expr* arg : args) { + if (!check_eq_compat(arg, v, eq, all)) { + return false; + } + } + return true; + } + + void hoist_nnf(goal const& g, expr* f, vector & path, unsigned idx, unsigned depth) { + if (depth > 4) { + return; + } + app_ref var(m()); + expr_ref def(m()); + proof_ref pr(m()); + expr_ref_vector args(m()); + expr* f1 = nullptr; + + if (m().is_not(f, f1) && m().is_or(f1)) { + flatten_and(f, args); + for (unsigned i = 0; i < args.size(); ++i) { + expr* arg = args.get(i), *lhs = nullptr, *rhs = nullptr; + if (m().is_eq(arg, lhs, rhs)) { + if (trivial_solve1(lhs, rhs, var, def, pr) && is_compatible(g, idx, path, var, arg)) { + insert_solution(g, idx, arg, var, def, pr); + } + else if (trivial_solve1(rhs, lhs, var, def, pr) && is_compatible(g, idx, path, var, arg)) { + insert_solution(g, idx, arg, var, def, pr); + } + else { + IF_VERBOSE(10000, + verbose_stream() << "eq not solved " << mk_pp(arg, m()) << "\n"; + verbose_stream() << is_uninterp_const(lhs) << " " << !m_candidate_vars.is_marked(lhs) << " " + << !occurs(lhs, rhs) << " " << check_occs(lhs) << "\n";); + } + } + else { + path.push_back(nnf_context(true, args, i)); + hoist_nnf(g, arg, path, idx, depth + 1); + path.pop_back(); + } + } + } + else if (m().is_or(f)) { + flatten_or(f, args); + for (unsigned i = 0; i < args.size(); ++i) { + path.push_back(nnf_context(false, args, i)); + hoist_nnf(g, args.get(i), path, idx, depth + 1); + path.pop_back(); + } + } + } + + void collect_hoist(goal const& g) { + unsigned size = g.size(); + vector path; + for (unsigned idx = 0; idx < size; idx++) { + checkpoint(); + hoist_nnf(g, g.form(idx), path, idx, 0); + } + } + + void distribute_and_or(goal & g) { + unsigned size = g.size(); + hoist_rewriter_star rw(m()); + th_rewriter thrw(m()); + expr_ref tmp(m()), tmp2(m()); + for (unsigned idx = 0; idx < size; idx++) { + checkpoint(); + expr* f = g.form(idx); + thrw(f, tmp); + rw(tmp, tmp2); + g.update(idx, tmp2); + } + + } void sort_vars() { SASSERT(m_candidates.size() == m_vars.size()); @@ -564,6 +742,10 @@ class solve_eqs_tactic : public tactic { ++idx; } + IF_VERBOSE(10000, + verbose_stream() << "ordered vars: "; + for (app* v : m_ordered_vars) verbose_stream() << mk_pp(v, m()) << " "; + verbose_stream() << "\n";); TRACE("solve_eqs", tout << "ordered vars:\n"; for (app* v : m_ordered_vars) { @@ -645,12 +827,6 @@ class solve_eqs_tactic : public tactic { } m_r->operator()(f, new_f, new_pr, new_dep); -#if 0 - pb_util pb(m()); - if (pb.is_ge(f) && f != new_f) { - IF_VERBOSE(0, verbose_stream() << mk_ismt2_pp(f, m()) << "\n--->\n" << mk_ismt2_pp(new_f, m()) << "\n"); - } -#endif TRACE("solve_eqs_subst", tout << mk_ismt2_pp(f, m()) << "\n--->\n" << mk_ismt2_pp(new_f, m()) << "\n";); m_num_steps += m_r->get_num_steps() + 1; @@ -754,8 +930,14 @@ class solve_eqs_tactic : public tactic { m_subst = alloc(expr_substitution, m(), m_produce_unsat_cores, m_produce_proofs); m_norm_subst = alloc(expr_substitution, m(), m_produce_unsat_cores, m_produce_proofs); while (true) { + if (m_context_solve) { + distribute_and_or(*(g.get())); + } collect_num_occs(*g); collect(*g); + if (m_context_solve) { + collect_hoist(*g); + } if (m_subst->empty()) break; sort_vars(); @@ -799,10 +981,11 @@ public: m_imp->updt_params(p); } - void collect_param_descrs(param_descrs & r) override { + void collect_param_descrs(param_descrs & r) override { r.insert("solve_eqs_max_occs", CPK_UINT, "(default: infty) maximum number of occurrences for considering a variable for gaussian eliminations."); r.insert("theory_solver", CPK_BOOL, "(default: true) use theory solvers."); r.insert("ite_solver", CPK_BOOL, "(default: true) use if-then-else solver."); + r.insert("context_solve", CPK_BOOL, "(default: false) solve equalities under disjunctions."); } void operator()(goal_ref const & in, diff --git a/src/tactic/fd_solver/bounded_int2bv_solver.cpp b/src/tactic/fd_solver/bounded_int2bv_solver.cpp index 8791a6282..0decaec82 100644 --- a/src/tactic/fd_solver/bounded_int2bv_solver.cpp +++ b/src/tactic/fd_solver/bounded_int2bv_solver.cpp @@ -137,9 +137,9 @@ public: } } - lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { + lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) override { flush_assertions(); - return m_solver->check_sat(num_assumptions, assumptions); + return m_solver->check_sat_core(num_assumptions, assumptions); } void updt_params(params_ref const & p) override { solver::updt_params(p); m_solver->updt_params(p); } @@ -155,6 +155,16 @@ public: if (mc) (*mc)(mdl); } } + void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { + m_solver->get_levels(vars, depth); + } + expr_ref_vector get_trail() override { + return m_solver->get_trail(); + } + void set_activity(expr* var, double activity) override { + m_solver->set_activity(var, activity); + } + model_converter* external_model_converter() const { return concat(mc0(), local_model_converter()); } diff --git a/src/tactic/fd_solver/enum2bv_solver.cpp b/src/tactic/fd_solver/enum2bv_solver.cpp index 185f23d13..b232d8ea3 100644 --- a/src/tactic/fd_solver/enum2bv_solver.cpp +++ b/src/tactic/fd_solver/enum2bv_solver.cpp @@ -78,9 +78,9 @@ public: m_rewriter.pop(n); } - lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { + lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) override { m_solver->updt_params(get_params()); - return m_solver->check_sat(num_assumptions, assumptions); + return m_solver->check_sat_core(num_assumptions, assumptions); } void updt_params(params_ref const & p) override { solver::updt_params(p); m_solver->updt_params(p); } @@ -178,7 +178,17 @@ public: return r; } + void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { + m_solver->get_levels(vars, depth); + } + expr_ref_vector get_trail() override { + return m_solver->get_trail(); + } + + void set_activity(expr* var, double activity) override { + m_solver->set_activity(var, activity); + } unsigned get_num_assertions() const override { return m_solver->get_num_assertions(); diff --git a/src/tactic/fd_solver/pb2bv_solver.cpp b/src/tactic/fd_solver/pb2bv_solver.cpp index fd4fb8e73..c6866ecf4 100644 --- a/src/tactic/fd_solver/pb2bv_solver.cpp +++ b/src/tactic/fd_solver/pb2bv_solver.cpp @@ -75,9 +75,9 @@ public: m_rewriter.pop(n); } - lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { + lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) override { flush_assertions(); - return m_solver->check_sat(num_assumptions, assumptions); + return m_solver->check_sat_core(num_assumptions, assumptions); } void updt_params(params_ref const & p) override { solver::updt_params(p); m_rewriter.updt_params(p); m_solver->updt_params(p); } @@ -97,9 +97,22 @@ public: } } + void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { + m_solver->get_levels(vars, depth); + } + + expr_ref_vector get_trail() override { + return m_solver->get_trail(); + } + + void set_activity(expr* var, double activity) override { + m_solver->set_activity(var, activity); + } + model_converter* external_model_converter() const{ return concat(mc0(), local_model_converter()); } + model_converter_ref get_model_converter() const override { model_converter_ref mc = external_model_converter(); mc = concat(mc.get(), m_solver->get_model_converter().get()); diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index dfe63e29a..c2d93fa2a 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -20,6 +20,7 @@ Revision History: #include "ast/ast_smt2_pp.h" #include "ast/for_each_expr.h" #include "ast/well_sorted.h" +#include "ast/display_dimacs.h" #include "tactic/goal.h" goal::precision goal::mk_union(precision p1, precision p2) { @@ -262,14 +263,14 @@ void goal::assert_expr(expr * f, expr_dependency * d) { assert_expr(f, proofs_enabled() ? m().mk_asserted(f) : nullptr, d); } -void goal::get_formulas(ptr_vector & result) { +void goal::get_formulas(ptr_vector & result) const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { result.push_back(form(i)); } } -void goal::get_formulas(expr_ref_vector & result) { +void goal::get_formulas(expr_ref_vector & result) const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { result.push_back(form(i)); @@ -434,62 +435,9 @@ void goal::display_ll(std::ostream & out) const { \brief Assumes that the formula is already in CNF. */ void goal::display_dimacs(std::ostream & out) const { - obj_map expr2var; - unsigned num_vars = 0; - unsigned num_cls = size(); - for (unsigned i = 0; i < num_cls; i++) { - expr * f = form(i); - unsigned num_lits; - expr * const * lits; - if (m().is_or(f)) { - num_lits = to_app(f)->get_num_args(); - lits = to_app(f)->get_args(); - } - else { - num_lits = 1; - lits = &f; - } - for (unsigned j = 0; j < num_lits; j++) { - expr * l = lits[j]; - if (m().is_not(l)) - l = to_app(l)->get_arg(0); - if (expr2var.contains(l)) - continue; - num_vars++; - expr2var.insert(l, num_vars); - } - } - out << "p cnf " << num_vars << " " << num_cls << "\n"; - for (unsigned i = 0; i < num_cls; i++) { - expr * f = form(i); - unsigned num_lits; - expr * const * lits; - if (m().is_or(f)) { - num_lits = to_app(f)->get_num_args(); - lits = to_app(f)->get_args(); - } - else { - num_lits = 1; - lits = &f; - } - for (unsigned j = 0; j < num_lits; j++) { - expr * l = lits[j]; - if (m().is_not(l)) { - out << "-"; - l = to_app(l)->get_arg(0); - } - unsigned id = UINT_MAX; - expr2var.find(l, id); - SASSERT(id != UINT_MAX); - out << id << " "; - } - out << "0\n"; - } - for (auto const& kv : expr2var) { - expr* key = kv.m_key; - if (is_app(key)) - out << "c " << kv.m_value << " " << to_app(key)->get_decl()->get_name() << "\n"; - } + expr_ref_vector fmls(m()); + get_formulas(fmls); + ::display_dimacs(out, fmls); } unsigned goal::num_exprs() const { diff --git a/src/tactic/goal.h b/src/tactic/goal.h index fa2f16eb6..33f9298ab 100644 --- a/src/tactic/goal.h +++ b/src/tactic/goal.h @@ -126,8 +126,8 @@ public: void update(unsigned i, expr * f, proof * pr = nullptr, expr_dependency * dep = nullptr); - void get_formulas(ptr_vector & result); - void get_formulas(expr_ref_vector & result); + void get_formulas(ptr_vector & result) const; + void get_formulas(expr_ref_vector & result) const; void elim_true(); void elim_redundancies(); diff --git a/src/tactic/nlsat_smt/nl_purify_tactic.cpp b/src/tactic/nlsat_smt/nl_purify_tactic.cpp deleted file mode 100644 index a02c2d327..000000000 --- a/src/tactic/nlsat_smt/nl_purify_tactic.cpp +++ /dev/null @@ -1,799 +0,0 @@ -/*++ -Copyright (c) 2015 Microsoft Corporation - -Module Name: - - nl_purify_tactic.cpp - -Abstract: - - Tactic for purifying quantifier-free formulas that mix QF_NRA and other theories. - It is designed to allow cooperation between the nlsat solver and other theories - in a decoupled way. - - Let goal be formula F. - Let NL goal be formula G. - Assume F is in NNF. - Assume F does not contain mix of real/integers. - Assume F is quantifier-free (please, otherwise we need to reprocess from instantiated satisfiable formula) - - For each atomic nl formula f, - - introduce a propositional variable p - - replace f by p - - add clauses p => f to G - - For each interface term t, - - introduce interface variable v (or use t if it is already a variable) - - replace t by v - - Check satisfiability of G. - If satisfiable, then check assignment to p and interface equalities on F - If unsat: - Retrieve core and add core to G. - else: - For interface equalities from model of F that are not equal in G, add - For interface variables that are equal under one model, but not the other model, - create interface predicate p_vw => v = w, add to both F, G. - Add interface equations to assumptions, recheck F. - If unsat retrieve core add to G. - -Author: - - Nikolaj Bjorner (nbjorner) 2015-5-5. - -Revision History: - ---*/ -#include "tactic/tactical.h" -#include "tactic/nlsat_smt/nl_purify_tactic.h" -#include "smt/tactic/smt_tactic.h" -#include "ast/rewriter/rewriter.h" -#include "nlsat/tactic/nlsat_tactic.h" -#include "tactic/filter_model_converter.h" -#include "util/obj_pair_hashtable.h" -#include "ast/rewriter/rewriter_def.h" -#include "ast/ast_pp.h" -#include "util/trace.h" -#include "smt/smt_solver.h" -#include "solver/solver.h" -#include "model/model_smt2_pp.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "ast/ast_util.h" -#include "solver/solver2tactic.h" - -class nl_purify_tactic : public tactic { - - enum polarity_t { - pol_pos, - pol_neg, - pol_dual - }; - - ast_manager & m; - arith_util m_util; - params_ref m_params; - bool m_produce_proofs; - ref m_fmc; - tactic_ref m_nl_tac; // nlsat tactic - goal_ref m_nl_g; // nlsat goal - ref m_solver; // SMT solver - expr_ref_vector m_eq_preds; // predicates for equality between pairs of interface variables - svector m_eq_values; // truth value of the equality predicates in nlsat - app_ref_vector m_new_reals; // interface real variables - app_ref_vector m_new_preds; // abstraction predicates for smt_solver (hide real constraints) - expr_ref_vector m_asms; // assumptions to pass to SMT solver - ptr_vector m_ctx_asms; // assumptions passed by context - obj_hashtable m_ctx_asms_set; // assumptions passed by context - obj_hashtable m_used_asms; - obj_map m_bool2dep; - obj_pair_map m_eq_pairs; // map pairs of interface variables to auxiliary predicates - obj_map m_interface_cache; // map of compound real expression to interface variable. - obj_map m_polarities; // polarities of sub-expressions - -public: - struct rw_cfg : public default_rewriter_cfg { - enum mode_t { - mode_interface_var, - mode_bool_preds - }; - ast_manager& m; - nl_purify_tactic & m_owner; - app_ref_vector& m_new_reals; - app_ref_vector& m_new_preds; - obj_map& m_polarities; - obj_map& m_interface_cache; - expr_ref_vector m_args; - proof_ref_vector m_proofs; - mode_t m_mode; - - rw_cfg(nl_purify_tactic & o): - m(o.m), - m_owner(o), - m_new_reals(o.m_new_reals), - m_new_preds(o.m_new_preds), - m_polarities(o.m_polarities), - m_interface_cache(o.m_interface_cache), - m_args(m), - m_proofs(m), - m_mode(mode_interface_var) { - } - - virtual ~rw_cfg() {} - - arith_util & u() { return m_owner.m_util; } - - expr * mk_interface_var(expr* arg, proof_ref& arg_pr) { - expr* r; - if (m_interface_cache.find(arg, r)) { - return r; - } - if (is_uninterp_const(arg)) { - m_interface_cache.insert(arg, arg); - return arg; - } - r = m.mk_fresh_const(nullptr, u().mk_real()); - m_new_reals.push_back(to_app(r)); - m_owner.m_fmc->insert(to_app(r)->get_decl()); - m_interface_cache.insert(arg, r); - expr_ref eq(m); - eq = m.mk_eq(r, arg); - if (is_real_expression(arg)) { - m_owner.m_nl_g->assert_expr(eq); // m.mk_oeq(r, arg) - } - else { - m_owner.m_solver->assert_expr(eq); - } - if (m_owner.m_produce_proofs) { - arg_pr = m.mk_oeq(arg, r); - } - return r; - } - - bool is_real_expression(expr* e) { - return is_app(e) && (to_app(e)->get_family_id() == u().get_family_id()); - } - - void mk_interface_bool(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref& pr) { - expr_ref old_pred(m.mk_app(f, num, args), m); - polarity_t pol = m_polarities.find(old_pred); - result = m.mk_fresh_const(nullptr, m.mk_bool_sort()); - m_polarities.insert(result, pol); - m_new_preds.push_back(to_app(result)); - m_owner.m_fmc->insert(to_app(result)->get_decl()); - if (pol != pol_neg) { - m_owner.m_nl_g->assert_expr(m.mk_or(m.mk_not(result), old_pred)); - } - if (pol != pol_pos) { - m_owner.m_nl_g->assert_expr(m.mk_or(result, m.mk_not(old_pred))); - } - if (m_owner.m_produce_proofs) { - pr = m.mk_oeq(old_pred, result); - } - TRACE("nlsat_smt", tout << old_pred << " : " << result << "\n";); - } - - bool reduce_quantifier(quantifier * old_q, - expr * new_body, - expr * const * new_patterns, - expr * const * new_no_patterns, - expr_ref & result, - proof_ref & result_pr) { - throw tactic_exception("quantifiers are not supported in mixed-mode nlsat engine"); - } - - br_status reduce_app(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref & pr) { - if (m_mode == mode_bool_preds) { - return reduce_app_bool(f, num, args, result, pr); - } - else { - return reduce_app_real(f, num, args, result, pr); - } - } - - br_status reduce_app_bool(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref & pr) { - if (f->get_family_id() == m.get_basic_family_id()) { - if (f->get_decl_kind() == OP_EQ && u().is_real(args[0])) { - mk_interface_bool(f, num, args, result, pr); - return BR_DONE; - } - else { - return BR_FAILED; - } - } - if (f->get_family_id() == u().get_family_id()) { - switch (f->get_decl_kind()) { - case OP_LE: case OP_GE: case OP_LT: case OP_GT: - // these are the only real cases of non-linear atomic formulas besides equality. - mk_interface_bool(f, num, args, result, pr); - return BR_DONE; - default: - return BR_FAILED; - } - } - return BR_FAILED; - } - - // (+ (f x) y) - // (f (+ x y)) - // - bool is_arith_op(expr* e) { - return is_app(e) && to_app(e)->get_family_id() == u().get_family_id(); - } - - br_status reduce_app_real(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref & pr) { - bool has_interface = false; - bool is_arith = false; - if (f->get_family_id() == u().get_family_id()) { - switch (f->get_decl_kind()) { - case OP_NUM: - case OP_IRRATIONAL_ALGEBRAIC_NUM: - return BR_FAILED; - default: - is_arith = true; - break; - } - } - m_args.reset(); - m_proofs.reset(); - for (unsigned i = 0; i < num; ++i) { - expr* arg = args[i]; - proof_ref arg_pr(m); - if (is_arith && !is_arith_op(arg)) { - has_interface = true; - m_args.push_back(mk_interface_var(arg, arg_pr)); - } - else if (!is_arith && u().is_real(arg)) { - has_interface = true; - m_args.push_back(mk_interface_var(arg, arg_pr)); - } - else { - m_args.push_back(arg); - } - if (arg_pr) { - m_proofs.push_back(arg_pr); - } - } - if (has_interface) { - result = m.mk_app(f, num, m_args.c_ptr()); - if (m_owner.m_produce_proofs) { - pr = m.mk_oeq_congruence(m.mk_app(f, num, args), to_app(result), m_proofs.size(), m_proofs.c_ptr()); - } - TRACE("nlsat_smt", tout << result << "\n";); - return BR_DONE; - } - else { - return BR_FAILED; - } - } - }; - -private: - - class rw : public rewriter_tpl { - rw_cfg m_cfg; - public: - rw(nl_purify_tactic & o): - rewriter_tpl(o.m, o.m_produce_proofs, m_cfg), - m_cfg(o) { - } - void set_bool_mode() { - m_cfg.m_mode = rw_cfg::mode_bool_preds; - } - void set_interface_var_mode() { - m_cfg.m_mode = rw_cfg::mode_interface_var; - } - }; - - - arith_util & u() { return m_util; } - - void check_point() { - if (m.canceled()) { - throw tactic_exception(Z3_CANCELED_MSG); - } - } - - void display_result(std::ostream& out, goal_ref_buffer const& result) { - for (unsigned i = 0; i < result.size(); ++i) { - result[i]->display_with_dependencies(out << "goal\n"); - } - } - - void update_eq_values(model_ref& mdl) { - expr_ref tmp(m); - for (unsigned i = 0; i < m_eq_preds.size(); ++i) { - expr* pred = m_eq_preds[i].get(); - m_eq_values[i] = l_undef; - if (mdl->eval(pred, tmp)) { - if (m.is_true(tmp)) { - m_eq_values[i] = l_true; - } - else if (m.is_false(tmp)) { - m_eq_values[i] = l_false; - } - } - } - } - - void solve( - goal_ref const& g, - goal_ref_buffer& result, - expr_dependency_ref& core, - model_converter_ref& mc) { - - while (true) { - check_point(); - TRACE("nlsat_smt", m_solver->display(tout << "SMT:\n"); m_nl_g->display(tout << "\nNL:\n"); ); - goal_ref tmp_nl = alloc(goal, m, true, false); - model_converter_ref nl_mc; - proof_converter_ref nl_pc; - expr_dependency_ref nl_core(m); - result.reset(); - tmp_nl->copy_from(*m_nl_g.get()); - (*m_nl_tac)(tmp_nl, result, nl_mc, nl_pc, nl_core); - - if (is_decided_unsat(result)) { - core2result(core, g, result); - TRACE("nlsat_smt", tout << "unsat\n";); - break; - } - if (!is_decided_sat(result)) { - TRACE("nlsat_smt", tout << "not a unit\n";); - break; - } - // extract evaluation on interface variables. - // assert booleans that evaluate to true. - // assert equalities between equal interface real variables. - - model_ref mdl_nl, mdl_smt; - if (nl_mc.get()) { - model_converter2model(m, nl_mc.get(), mdl_nl); - update_eq_values(mdl_nl); - enforce_equalities(mdl_nl, m_nl_g); - - setup_assumptions(mdl_nl); - - TRACE("nlsat_smt", - model_smt2_pp(tout << "nl model\n", m, *mdl_nl.get(), 0); - m_solver->display(tout << "smt goal:\n"); tout << "\n";); - } - result.reset(); - lbool r = m_solver->check_sat(m_asms.size(), m_asms.c_ptr()); - if (r == l_false) { - // extract the core from the result - ptr_vector ecore, asms; - expr_ref_vector clause(m); - expr_ref fml(m); - get_unsat_core(ecore, asms); - - // - // assumptions should also be used for the nlsat tactic, - // but since it does not support assumptions at this time - // we overapproximate the necessary core and accumulate - // all assumptions that are ever used. - // - for (unsigned i = 0; i < asms.size(); ++i) { - m_used_asms.insert(asms[i]); - } - if (ecore.empty()) { - core2result(core, g, result); - break; - } - for (unsigned i = 0; i < ecore.size(); ++i) { - clause.push_back(mk_not(m, ecore[i])); - } - fml = mk_or(m, clause.size(), clause.c_ptr()); - m_nl_g->assert_expr(fml); - continue; - } - else if (r == l_true) { - m_solver->get_model(mdl_smt); - if (enforce_equalities(mdl_smt, m_nl_g)) { - // SMT enforced a new equality that wasn't true for nlsat. - continue; - } - TRACE("nlsat_smt", - m_fmc->display(tout << "joint state is sat\n"); - nl_mc->display(tout << "nl\n");); - if (mdl_nl.get()) { - merge_models(*mdl_nl.get(), mdl_smt); - } - mc = m_fmc.get(); - apply(mc, mdl_smt, 0); - mc = model2model_converter(mdl_smt.get()); - result.push_back(alloc(goal, m)); - } - else { - TRACE("nlsat_smt", tout << "unknown\n";); - } - break; - } - TRACE("nlsat_smt", display_result(tout, result);); - } - - void get_unsat_core(ptr_vector& core, ptr_vector& asms) { - m_solver->get_unsat_core(core); - for (unsigned i = 0; i < core.size(); ++i) { - if (m_ctx_asms_set.contains(core[i])) { - asms.push_back(core[i]); - core[i] = core.back(); - core.pop_back(); - --i; - } - } - } - - void core2result(expr_dependency_ref & lcore, goal_ref const& g, goal_ref_buffer& result) { - result.reset(); - proof * pr = nullptr; - lcore = nullptr; - g->reset(); - obj_hashtable::iterator it = m_used_asms.begin(), end = m_used_asms.end(); - for (; it != end; ++it) { - lcore = m.mk_join(lcore, m.mk_leaf(m_bool2dep.find(*it))); - } - g->assert_expr(m.mk_false(), pr, lcore); - TRACE("nlsat_smt", g->display_with_dependencies(tout);); - result.push_back(g.get()); - } - - void setup_assumptions(model_ref& mdl) { - m_asms.reset(); - m_asms.append(m_ctx_asms.size(), m_ctx_asms.c_ptr()); - app_ref_vector const& fresh_preds = m_new_preds; - expr_ref tmp(m); - for (unsigned i = 0; i < fresh_preds.size(); ++i) { - expr* pred = fresh_preds[i]; - if (mdl->eval(pred, tmp)) { - polarity_t pol = m_polarities.find(pred); - // if assumption literals are used to satisfy NL state, - // we have to assume them when satisfying SMT state - if (pol != pol_neg && m.is_false(tmp)) { - m_asms.push_back(m.mk_not(pred)); - } - else if (pol != pol_pos && m.is_true(tmp)) { - m_asms.push_back(pred); - } - } - } - for (unsigned i = 0; i < m_eq_preds.size(); ++i) { - expr* pred = m_eq_preds[i].get(); - switch (m_eq_values[i]) { - case l_true: - m_asms.push_back(pred); - break; - case l_false: - m_asms.push_back(m.mk_not(pred)); - break; - default: - break; - } - } - TRACE("nlsat_smt", - tout << "assumptions:\n" << m_asms << "\n";); - } - - bool enforce_equalities(model_ref& mdl, goal_ref const& nl_g) { - TRACE("nlsat_smt", tout << "Enforce equalities " << m_interface_cache.size() << "\n";); - bool new_equality = false; - expr_ref_vector nums(m); - obj_map num2var; - obj_map::iterator it = m_interface_cache.begin(), end = m_interface_cache.end(); - for (; it != end; ++it) { - expr_ref r(m); - expr* v, *w, *pred; - w = it->m_value; - VERIFY(mdl->eval(w, r)); - TRACE("nlsat_smt", tout << mk_pp(w, m) << " |-> " << r << "\n";); - nums.push_back(r); - if (num2var.find(r, v)) { - if (!m_eq_pairs.find(v, w, pred)) { - pred = m.mk_fresh_const(nullptr, m.mk_bool_sort()); - m_eq_preds.push_back(pred); - m_eq_values.push_back(l_true); - m_fmc->insert(to_app(pred)->get_decl()); - nl_g->assert_expr(m.mk_or(m.mk_not(pred), m.mk_eq(w, v))); - nl_g->assert_expr(m.mk_or(pred, m.mk_not(m.mk_eq(w, v)))); - m_solver->assert_expr(m.mk_iff(pred, m.mk_eq(w, v))); - new_equality = true; - m_eq_pairs.insert(v, w, pred); - } - else { - // interface equality is already enforced. - } - } - else { - num2var.insert(r, w); - } - } - return new_equality; - } - - void merge_models(model const& mdl_nl, model_ref& mdl_smt) { - expr_safe_replace num2num(m); - expr_ref result(m), val2(m); - expr_ref_vector args(m); - unsigned sz = mdl_nl.get_num_constants(); - for (unsigned i = 0; i < sz; ++i) { - func_decl* v = mdl_nl.get_constant(i); - if (u().is_real(v->get_range())) { - expr* val = mdl_nl.get_const_interp(v); - if (mdl_smt->eval(v, val2)) { - if (val != val2) { - num2num.insert(val2, val); - } - } - } - } - sz = mdl_smt->get_num_functions(); - for (unsigned i = 0; i < sz; ++i) { - func_decl* f = mdl_smt->get_function(i); - if (has_real(f)) { - unsigned arity = f->get_arity(); - func_interp* f1 = mdl_smt->get_func_interp(f); - func_interp* f2 = alloc(func_interp, m, f->get_arity()); - for (unsigned j = 0; j < f1->num_entries(); ++j) { - args.reset(); - func_entry const* entry = f1->get_entry(j); - for (unsigned k = 0; k < arity; ++k) { - translate(num2num, entry->get_arg(k), result); - args.push_back(result); - } - translate(num2num, entry->get_result(), result); - f2->insert_entry(args.c_ptr(), result); - } - translate(num2num, f1->get_else(), result); - f2->set_else(result); - mdl_smt->register_decl(f, f2); - } - } - mdl_smt->copy_const_interps(mdl_nl); - } - - bool has_real(func_decl* f) { - for (unsigned i = 0; i < f->get_arity(); ++i) { - if (u().is_real(f->get_domain(i))) return true; - } - return u().is_real(f->get_range()); - } - - void translate(expr_safe_replace& num2num, expr* e, expr_ref& result) { - result = nullptr; - if (e) { - num2num(e, result); - } - } - - void get_polarities(goal const& g) { - ptr_vector forms; - svector pols; - unsigned sz = g.size(); - for (unsigned i = 0; i < sz; ++i) { - forms.push_back(g.form(i)); - pols.push_back(pol_pos); - } - polarity_t p, q; - while (!forms.empty()) { - expr* e = forms.back(); - p = pols.back(); - forms.pop_back(); - pols.pop_back(); - if (m_polarities.find(e, q)) { - if (p == q || q == pol_dual) continue; - p = pol_dual; - } - TRACE("nlsat_smt_verbose", tout << mk_pp(e, m) << "\n";); - m_polarities.insert(e, p); - if (is_quantifier(e) || is_var(e)) { - throw tactic_exception("nl-purify tactic does not support quantifiers"); - } - SASSERT(is_app(e)); - app* a = to_app(e); - func_decl* f = a->get_decl(); - if (f->get_family_id() == m.get_basic_family_id() && p != pol_dual) { - switch(f->get_decl_kind()) { - case OP_NOT: - p = neg(p); - break; - case OP_AND: - case OP_OR: - break; - default: - p = pol_dual; - break; - } - } - else { - p = pol_dual; - } - for (unsigned i = 0; i < a->get_num_args(); ++i) { - forms.push_back(a->get_arg(i)); - pols.push_back(p); - } - } - } - - polarity_t neg(polarity_t p) { - switch (p) { - case pol_pos: return pol_neg; - case pol_neg: return pol_pos; - case pol_dual: return pol_dual; - } - return pol_dual; - } - - polarity_t join(polarity_t p, polarity_t q) { - if (p == q) return p; - return pol_dual; - } - - void rewrite_goal(rw& r, goal_ref const& g) { - r.reset(); - expr_ref new_curr(m); - proof_ref new_pr(m); - unsigned sz = g->size(); - for (unsigned i = 0; i < sz; i++) { - expr * curr = g->form(i); - r(curr, new_curr, new_pr); - if (m_produce_proofs) { - proof * pr = g->pr(i); - new_pr = m.mk_modus_ponens(pr, new_pr); - } - g->update(i, new_curr, new_pr, g->dep(i)); - } - } - - void remove_pure_arith(goal_ref const& g) { - obj_map is_pure; - unsigned sz = g->size(); - for (unsigned i = 0; i < sz; i++) { - expr * curr = g->form(i); - if (is_pure_arithmetic(is_pure, curr)) { - m_nl_g->assert_expr(curr, g->pr(i), g->dep(i)); - g->update(i, m.mk_true(), g->pr(i), g->dep(i)); - } - } - } - - bool is_pure_arithmetic(obj_map& is_pure, expr* e0) { - ptr_vector todo; - todo.push_back(e0); - while (!todo.empty()) { - expr* e = todo.back(); - if (is_pure.contains(e)) { - todo.pop_back(); - continue; - } - if (!is_app(e)) { - todo.pop_back(); - is_pure.insert(e, false); - continue; - } - app* a = to_app(e); - bool pure = false, all_found = true, p; - pure |= (a->get_family_id() == u().get_family_id()) && u().is_real(a); - pure |= (m.is_eq(e) && u().is_real(a->get_arg(0))); - pure |= (a->get_family_id() == u().get_family_id()) && m.is_bool(a) && u().is_real(a->get_arg(0)); - pure |= (a->get_family_id() == m.get_basic_family_id()); - pure |= is_uninterp_const(a) && u().is_real(a); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - if (!is_pure.find(a->get_arg(i), p)) { - todo.push_back(a->get_arg(i)); - all_found = false; - } - else { - pure &= p; - } - } - if (all_found) { - is_pure.insert(e, pure); - todo.pop_back(); - } - } - return is_pure.find(e0); - } - -public: - - nl_purify_tactic(ast_manager & m, params_ref const& p): - m(m), - m_util(m), - m_params(p), - m_fmc(nullptr), - m_nl_tac(mk_nlsat_tactic(m, p)), - m_nl_g(nullptr), - m_solver(mk_smt_solver(m, p, symbol::null)), - m_eq_preds(m), - m_new_reals(m), - m_new_preds(m), - m_asms(m) - {} - - ~nl_purify_tactic() override {} - - void updt_params(params_ref const & p) override { - m_params = p; - } - - tactic * translate(ast_manager& m) override { - return alloc(nl_purify_tactic, m, m_params); - } - - void collect_statistics(statistics & st) const override { - m_nl_tac->collect_statistics(st); - m_solver->collect_statistics(st); - } - - void reset_statistics() override { - m_nl_tac->reset_statistics(); - } - - - void cleanup() override { - m_solver = mk_smt_solver(m, m_params, symbol::null); - m_nl_tac->cleanup(); - m_eq_preds.reset(); - m_eq_values.reset(); - m_new_reals.reset(); - m_new_preds.reset(); - m_eq_pairs.reset(); - m_polarities.reset(); - m_ctx_asms.reset(); - m_ctx_asms_set.reset(); - m_used_asms.reset(); - m_bool2dep.reset(); - } - - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - - tactic_report report("qfufnl-purify", *g); - TRACE("nlsat_smt", g->display(tout);); - - m_produce_proofs = g->proofs_enabled(); - mc = nullptr; pc = nullptr; core = nullptr; - - fail_if_proof_generation("qfufnra-purify", g); - // fail_if_unsat_core_generation("qfufnra-purify", g); - rw r(*this); - expr_ref_vector clauses(m); - m_nl_g = alloc(goal, m, true, false); - m_fmc = alloc(filter_model_converter, m); - - // first hoist interface variables, - // then annotate subformulas by polarities, - // finally extract polynomial inequalities by - // creating a place-holder predicate inside the - // original goal and extracting pure nlsat clauses. - r.set_interface_var_mode(); - rewrite_goal(r, g); - if (!g->unsat_core_enabled()) { - remove_pure_arith(g); - } - get_polarities(*g.get()); - r.set_bool_mode(); - rewrite_goal(r, g); - - extract_clauses_and_dependencies(g, clauses, m_ctx_asms, m_bool2dep, m_fmc); - - TRACE("nlsat_smt", tout << clauses << "\n";); - - for (unsigned i = 0; i < m_ctx_asms.size(); ++i) { - m_ctx_asms_set.insert(m_ctx_asms[i]); - } - - for (unsigned i = 0; i < clauses.size(); ++i) { - m_solver->assert_expr(clauses[i].get()); - } - g->inc_depth(); - solve(g, result, core, mc); - } -}; - - -tactic * mk_nl_purify_tactic(ast_manager& m, params_ref const& p) { - return alloc(nl_purify_tactic, m, p); -} diff --git a/src/tactic/portfolio/default_tactic.cpp b/src/tactic/portfolio/default_tactic.cpp index 23334bf07..c2bccd75b 100644 --- a/src/tactic/portfolio/default_tactic.cpp +++ b/src/tactic/portfolio/default_tactic.cpp @@ -35,7 +35,7 @@ Notes: tactic * mk_default_tactic(ast_manager & m, params_ref const & p) { tactic * st = using_params(and_then(mk_simplify_tactic(m), - cond(mk_is_propositional_probe(), if_no_proofs(mk_fd_tactic(m, p)), + cond(mk_and(mk_is_propositional_probe(), mk_not(mk_produce_proofs_probe())), mk_fd_tactic(m, p), cond(mk_is_qfbv_probe(), mk_qfbv_tactic(m), cond(mk_is_qfaufbv_probe(), mk_qfaufbv_tactic(m), cond(mk_is_qflia_probe(), mk_qflia_tactic(m), diff --git a/src/tactic/sls/sls_engine.cpp b/src/tactic/sls/sls_engine.cpp index 1285e46cf..f55aa41aa 100644 --- a/src/tactic/sls/sls_engine.cpp +++ b/src/tactic/sls/sls_engine.cpp @@ -440,7 +440,7 @@ lbool sls_engine::search() { // get candidate variables ptr_vector & to_evaluate = m_tracker.get_unsat_constants(m_assertions); - if (!to_evaluate.size()) + if (to_evaluate.empty()) { res = l_true; goto bailout; @@ -492,7 +492,7 @@ lbool sls_engine::search() { score = m_tracker.get_top_sum(); - // update assertion weights if a weigthing is enabled (sp < 1024) + // update assertion weights if a weighting is enabled (sp < 1024) if (m_paws) { for (unsigned i = 0; i < sz; i++) diff --git a/src/tactic/sls/sls_tracker.h b/src/tactic/sls/sls_tracker.h index b730e78da..e10c91729 100644 --- a/src/tactic/sls/sls_tracker.h +++ b/src/tactic/sls/sls_tracker.h @@ -1003,7 +1003,7 @@ public: } ptr_vector & get_unsat_constants_walksat(expr * e) { - if (!e || m_temp_constants.size()) + if (!e || !m_temp_constants.empty()) return m_temp_constants; ptr_vector const & this_decls = m_constants_occ.find(e); unsigned sz = this_decls.size(); diff --git a/src/tactic/smtlogics/qfaufbv_tactic.cpp b/src/tactic/smtlogics/qfaufbv_tactic.cpp index b35bc1f20..e3d7ec953 100644 --- a/src/tactic/smtlogics/qfaufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfaufbv_tactic.cpp @@ -24,13 +24,10 @@ Notes: #include "tactic/bv/max_bv_sharing_tactic.h" #include "tactic/bv/bv_size_reduction_tactic.h" #include "tactic/core/ctx_simplify_tactic.h" -#include "sat/tactic/sat_tactic.h" +#include "ackermannization/ackermannize_bv_tactic.h" #include "smt/tactic/smt_tactic.h" -tactic * mk_qfaufbv_tactic(ast_manager & m, params_ref const & p) { - params_ref main_p; - main_p.set_bool("elim_and", true); - main_p.set_bool("sort_store", true); +static tactic * mk_qfaufbv_preamble(ast_manager & m, params_ref const & p) { params_ref simp2_p = p; simp2_p.set_bool("som", true); @@ -39,26 +36,27 @@ tactic * mk_qfaufbv_tactic(ast_manager & m, params_ref const & p) { simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); - params_ref ctx_simp_p; - ctx_simp_p.set_uint("max_depth", 32); - ctx_simp_p.set_uint("max_steps", 5000000); - params_ref solver_p; - solver_p.set_bool("array.simplify", false); // disable array simplifications at old_simplify module + return and_then(mk_simplify_tactic(m), + mk_propagate_values_tactic(m), + mk_solve_eqs_tactic(m), + mk_elim_uncnstr_tactic(m), + // sound to use? if_no_proofs(if_no_unsat_cores(mk_reduce_args_tactic(m))), + if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))), + using_params(mk_simplify_tactic(m), simp2_p), + mk_max_bv_sharing_tactic(m), + if_no_proofs(if_no_unsat_cores(mk_ackermannize_bv_tactic(m, p))) + ); +} - tactic * preamble_st = and_then(mk_simplify_tactic(m), - mk_propagate_values_tactic(m), - // using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), - mk_solve_eqs_tactic(m), - mk_elim_uncnstr_tactic(m), - if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))), - using_params(mk_simplify_tactic(m), simp2_p), - mk_max_bv_sharing_tactic(m) - ); +tactic * mk_qfaufbv_tactic(ast_manager & m, params_ref const & p) { + params_ref main_p; + main_p.set_bool("elim_and", true); + main_p.set_bool("sort_store", true); - tactic * st = using_params(and_then(preamble_st, - using_params(mk_smt_tactic(m), solver_p)), - main_p); + tactic * preamble_st = mk_qfaufbv_preamble(m, p); + + tactic * st = using_params(and_then(preamble_st, mk_smt_tactic(m)), main_p); st->updt_params(p); return st; diff --git a/src/tactic/smtlogics/qfnia_tactic.cpp b/src/tactic/smtlogics/qfnia_tactic.cpp index d28034a1d..a9a6032f1 100644 --- a/src/tactic/smtlogics/qfnia_tactic.cpp +++ b/src/tactic/smtlogics/qfnia_tactic.cpp @@ -27,6 +27,7 @@ Notes: #include "sat/tactic/sat_tactic.h" #include "tactic/arith/nla2bv_tactic.h" #include "tactic/arith/lia2card_tactic.h" +#include "tactic/arith/card2bv_tactic.h" #include "tactic/core/ctx_simplify_tactic.h" #include "tactic/core/cofactor_term_ite_tactic.h" #include "nlsat/tactic/qfnra_nlsat_tactic.h" @@ -73,7 +74,8 @@ static tactic * mk_qfnia_premable(ast_manager & m, params_ref const & p_ref) { using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), using_params(mk_simplify_tactic(m), pull_ite_p), mk_elim_uncnstr_tactic(m), - mk_lia2card_tactic(m), + mk_lia2card_tactic(m), + mk_card2bv_tactic(m, p_ref), skip_if_failed(using_params(mk_cofactor_term_ite_tactic(m), elim_p))); } @@ -105,7 +107,9 @@ static tactic * mk_qfnia_nlsat_solver(ast_manager & m, params_ref const & p) { static tactic * mk_qfnia_smt_solver(ast_manager& m, params_ref const& p) { params_ref simp_p = p; simp_p.set_bool("som", true); // expand into sums of monomials - return and_then(using_params(mk_simplify_tactic(m), simp_p), mk_smt_tactic(m)); + return and_then( + using_params(mk_simplify_tactic(m), simp_p), + mk_smt_tactic(m)); } tactic * mk_qfnia_tactic(ast_manager & m, params_ref const & p) { @@ -113,6 +117,7 @@ tactic * mk_qfnia_tactic(ast_manager & m, params_ref const & p) { return and_then( mk_report_verbose_tactic("(qfnia-tactic)", 10), mk_qfnia_premable(m, p), + or_else(mk_qfnia_sat_solver(m, p), try_for(mk_qfnia_smt_solver(m, p), 2000), mk_qfnia_nlsat_solver(m, p), diff --git a/src/tactic/smtlogics/qfufbv_tactic.cpp b/src/tactic/smtlogics/qfufbv_tactic.cpp index 38eaa25fb..8dd77cba2 100644 --- a/src/tactic/smtlogics/qfufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfufbv_tactic.cpp @@ -121,7 +121,7 @@ private: tactic_ref t = mk_qfaufbv_tactic(m_m, m_p); sat = mk_tactic2solver(m_m, t.get(), m_p); } - SASSERT(sat != NULL); + SASSERT(sat != nullptr); sat->set_produce_models(true); return sat; } diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index 3b3c2444f..95eeaa251 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -39,13 +39,14 @@ struct tactic_report::imp { ~imp() { m_watch.stop(); double end_memory = static_cast(memory::get_allocation_size())/static_cast(1024*1024); - verbose_stream() << "(" << m_id - << " :num-exprs " << m_goal.num_exprs() - << " :num-asts " << m_goal.m().get_num_asts() - << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() - << " :before-memory " << std::fixed << std::setprecision(2) << m_start_memory - << " :after-memory " << std::fixed << std::setprecision(2) << end_memory - << ")" << std::endl; + IF_VERBOSE(0, + verbose_stream() << "(" << m_id + << " :num-exprs " << m_goal.num_exprs() + << " :num-asts " << m_goal.m().get_num_asts() + << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() + << " :before-memory " << std::fixed << std::setprecision(2) << m_start_memory + << " :after-memory " << std::fixed << std::setprecision(2) << end_memory + << ")" << std::endl); } }; @@ -191,7 +192,7 @@ lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, p return l_false; } else { - if (models_enabled && r.size() >= 1) { + if (models_enabled && !r.empty()) { model_converter_ref mc = r[0]->mc(); model_converter2model(m, mc.get(), md); if (mc) diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index 92cc7315f..dd6557dcc 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -78,7 +78,6 @@ protected: friend class nary_tactical; friend class binary_tactical; friend class unary_tactical; - friend class nl_purify_tactic; }; diff --git a/src/tactic/tactic_params.pyg b/src/tactic/tactic_params.pyg new file mode 100644 index 000000000..5dfc6e5e8 --- /dev/null +++ b/src/tactic/tactic_params.pyg @@ -0,0 +1,20 @@ + +def_module_params('tactic', + description='tactic parameters', + export=True, + params=(('solve_eqs.context_solve', BOOL, False, "solve equalities within disjunctions."), + ('solve_eqs.theory_solver', BOOL, True, "use theory solvers."), + ('solve_eqs.ite_solver', BOOL, True, "use if-then-else solvers."), + ('solve_eqs.max_occs', UINT, UINT_MAX, "maximum number of occurrences for considering a variable for gaussian eliminations."), + ('blast_term_ite.max_inflation', UINT, UINT_MAX, "multiplicative factor of initial term size."), + ('blast_term_ite.max_steps', UINT, UINT_MAX, "maximal number of steps allowed for tactic."), + ('propagate_values.max_rounds', UINT, 4, "maximal number of rounds to propagate values."), + # ('aig.per_assertion', BOOL, True, "process one assertion at a time"), + # ('add_bounds.lower, INT, -2, "lower bound to be added to unbounded variables."), + # ('add_bounds.upper, INT, 2, "upper bound to be added to unbounded variables."), + # ('fm.real_only', BOOL, True, "consider only real variables for FM"), + # ('fm.occ', BOOL, False, "consider inequalities occurring in clauses for FM."), + # ('fm.limit', UINT, 5000000, "maximal number of constraints, monomials, clauses visited during FM."), + # etc: lia2card, factor, nla2bv, normalize_bounds, pb2bv, purify_arith, bit_blaster, bv_bounds + )) + diff --git a/src/test/bit_vector.cpp b/src/test/bit_vector.cpp index 487f6cdd0..db6270b35 100644 --- a/src/test/bit_vector.cpp +++ b/src/test/bit_vector.cpp @@ -35,7 +35,7 @@ static void tst1() { } else if (op <= 3) { ENSURE(v1.size() == v2.size()); - if (v1.size() > 0) { + if (!v1.empty()) { bool val = (rand()%2) != 0; unsigned idx = rand()%v1.size(); ENSURE(v1.get(idx) == v2[idx]); @@ -46,7 +46,7 @@ static void tst1() { } else if (op <= 4) { ENSURE(v1.size() == v2.size()); - if (v1.size() > 0) { + if (!v1.empty()) { unsigned idx = rand()%v1.size(); VERIFY(v1.get(idx) == v2[idx]); } diff --git a/src/test/chashtable.cpp b/src/test/chashtable.cpp index 0ccf2c177..8e4dadf99 100644 --- a/src/test/chashtable.cpp +++ b/src/test/chashtable.cpp @@ -102,7 +102,7 @@ static void tst3() { ENSURE(t.contains(12)); t.erase(12); t.erase(10); - ENSURE(t.size() == 0); + ENSURE(t.empty()); ENSURE(t.empty()); ENSURE(t.used_slots() == 0); t.insert(10); diff --git a/src/test/cnf_backbones.cpp b/src/test/cnf_backbones.cpp index 10f3e56a7..9db15c304 100644 --- a/src/test/cnf_backbones.cpp +++ b/src/test/cnf_backbones.cpp @@ -90,7 +90,7 @@ static void track_clauses(sat::solver const& src, sat::clause * const * it = src.begin_clauses(); sat::clause * const * end = src.end_clauses(); svector bin_clauses; - src.collect_bin_clauses(bin_clauses, false); + src.collect_bin_clauses(bin_clauses, false, false); tracking_clauses.reserve(2*src.num_vars() + static_cast(end - it) + bin_clauses.size()); for (sat::bool_var v = 1; v < src.num_vars(); ++v) { diff --git a/src/test/dl_table.cpp b/src/test/dl_table.cpp index 326be5d04..8eb864f7b 100644 --- a/src/test/dl_table.cpp +++ b/src/test/dl_table.cpp @@ -1,8 +1,6 @@ /*++ Copyright (c) 2015 Microsoft Corporation --*/ -#if defined(_WINDOWS) || defined(_CYGWIN) - #include "muz/base/dl_context.h" #include "muz/rel/dl_table.h" #include "muz/fp/dl_register_engine.h" @@ -98,7 +96,3 @@ void test_dl_bitvector_table() { void tst_dl_table() { test_dl_bitvector_table(); } -#else -void tst_dl_table() { -} -#endif diff --git a/src/test/heap.cpp b/src/test/heap.cpp index 2c77b939e..6a5bc7b9f 100644 --- a/src/test/heap.cpp +++ b/src/test/heap.cpp @@ -88,7 +88,7 @@ static void tst2() { int_heap2 h(N); for (int i = 0; i < N * 10; i++) { - if (i % 1 == 0) std::cout << "i: " << i << std::endl; + // if (i % 1 == 0) std::cout << "i: " << i << std::endl; if (i % 1000 == 0) std::cout << "i: " << i << std::endl; int cmd = heap_rand() % 10; if (cmd <= 3) { diff --git a/src/test/lp/argument_parser.h b/src/test/lp/argument_parser.h index ce59632d2..f2a74f122 100644 --- a/src/test/lp/argument_parser.h +++ b/src/test/lp/argument_parser.h @@ -117,7 +117,7 @@ public: unknown_options.push_back(t); } } - if (unknown_options.size()) { + if (!unknown_options.empty()) { ret = "Unknown options:"; } for (const auto & unknownOption : unknown_options) { @@ -127,15 +127,15 @@ public: ret += "\n"; ret += "Usage:\n"; for (auto allowed_option : m_options) - ret += allowed_option.first + " " + (allowed_option.second.size() == 0 ? std::string("") : std::string("/") + allowed_option.second) + std::string("\n"); + ret += allowed_option.first + " " + (allowed_option.second.empty() ? std::string("") : std::string("/") + allowed_option.second) + std::string("\n"); for (auto s : m_options_with_after_string) { - ret += s.first + " " + (s.second.size() == 0? " \"option value\"":("\""+ s.second+"\"")) + "\n"; + ret += s.first + " " + (s.second.empty()? " \"option value\"":("\""+ s.second+"\"")) + "\n"; } return ret; } void print() { - if (m_used_options.size() == 0 && m_used_options_with_after_string.size() == 0 && m_free_args.size() == 0) { + if (m_used_options.empty() && m_used_options_with_after_string.empty() && m_free_args.empty()) { std::cout << "no options are given" << std::endl; return; } @@ -146,7 +146,7 @@ public: for (auto & t : m_used_options_with_after_string) { std::cout << t.first << " " << t.second << std::endl; } - if (m_free_args.size() > 0) { + if (!m_free_args.empty()) { std::cout << "free arguments are: " << std::endl; for (auto & t : m_free_args) { std::cout << t << " " << std::endl; diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 192bd46b0..f43aff668 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1091,7 +1091,7 @@ void lp_solver_test() { bool get_int_from_args_parser(const char * option, argument_parser & args_parser, unsigned & n) { std::string s = args_parser.get_option_value(option); - if (s.size() > 0) { + if (!s.empty()) { n = atoi(s.c_str()); return true; } @@ -1100,7 +1100,7 @@ bool get_int_from_args_parser(const char * option, argument_parser & args_parser bool get_double_from_args_parser(const char * option, argument_parser & args_parser, double & n) { std::string s = args_parser.get_option_value(option); - if (s.size() > 0) { + if (!s.empty()) { n = atof(s.c_str()); return true; } @@ -1830,7 +1830,7 @@ std::unordered_map * get_solution_from_glpsol_output(std::s return nullptr; } auto split = string_split(s, " \t", false); - if (split.size() == 0) { + if (split.empty()) { return ret; } @@ -2050,14 +2050,14 @@ void finalize(unsigned ret) { void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit, unsigned & max_iters) { std::string s = args_parser.get_option_value("--max_iters"); - if (s.size() > 0) { + if (!s.empty()) { max_iters = atoi(s.c_str()); } else { max_iters = 0; } std::string time_limit_string = args_parser.get_option_value("--time_limit"); - if (time_limit_string.size() > 0) { + if (!time_limit_string.empty()) { time_limit = atoi(time_limit_string.c_str()); } else { time_limit = 0; @@ -2156,7 +2156,7 @@ double get_lp_tst_cost(std::string file_name) { cost_string = str; } } - if (cost_string.size() == 0) { + if (cost_string.empty()) { std::cout << "cannot find the cost line in " << file_name << std::endl; throw 0; } @@ -2377,7 +2377,7 @@ std::unordered_map get_solution_map(lp_solver * reader) { std::string maxng = args_parser.get_option_value("--maxng"); - if (maxng.size() > 0) { + if (!maxng.empty()) { solver->settings().max_number_of_iterations_with_no_improvements = atoi(maxng.c_str()); } if (args_parser.option_is_used("-pd")){ @@ -2385,7 +2385,7 @@ void run_lar_solver(argument_parser & args_parser, lar_solver * solver, mps_read } std::string iter = args_parser.get_option_value("--max_iters"); - if (iter.size() > 0) { + if (!iter.empty()) { solver->settings().max_total_number_of_iterations = atoi(iter.c_str()); } if (args_parser.option_is_used("--compare_with_primal")){ @@ -2470,7 +2470,7 @@ vector get_file_names_from_file_list(std::string filelist) { std::string s = read_line(end, file); if (end) break; - if (s.size() == 0) + if (s.empty()) break; ret.push_back(s); } while (true); @@ -2480,13 +2480,13 @@ vector get_file_names_from_file_list(std::string filelist) { void test_lar_solver(argument_parser & args_parser) { std::string file_name = args_parser.get_option_value("--file"); - if (file_name.size() > 0) { + if (!file_name.empty()) { test_lar_on_file(file_name, args_parser); return; } std::string file_list = args_parser.get_option_value("--filelist"); - if (file_list.size() > 0) { + if (!file_list.empty()) { for (const std::string & fn : get_file_names_from_file_list(file_list)) test_lar_on_file(fn, args_parser); return; @@ -3600,7 +3600,7 @@ void test_lp_local(int argn, char**argv) { std::string lufile = args_parser.get_option_value("--checklu"); - if (lufile.size()) { + if (!lufile.empty()) { check_lu_from_file(lufile); return finalize(0); } @@ -3623,7 +3623,7 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } std::string file_list = args_parser.get_option_value("--filelist"); - if (file_list.size() > 0) { + if (!file_list.empty()) { for (const std::string & fn : get_file_names_from_file_list(file_list)) solve_mps(fn, args_parser); return finalize(0); @@ -3704,7 +3704,7 @@ void test_lp_local(int argn, char**argv) { bool dual = args_parser.option_is_used("--dual"); bool solve_for_rational = args_parser.option_is_used("--mpq"); std::string file_name = args_parser.get_option_value("--file"); - if (file_name.size() > 0) { + if (!file_name.empty()) { solve_mps(file_name, args_parser.option_is_used("--min"), max_iters, time_limit, solve_for_rational, dual, args_parser.option_is_used("--compare_with_primal"), args_parser); ret = 0; return finalize(ret); diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 16f44e3b3..4bce99765 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -52,7 +52,7 @@ namespace lp { std::string m_head; std::vector m_elems; void print() { - if (m_elems.size()) { + if (!m_elems.empty()) { std::cout << '('; std::cout << m_head << ' '; for (auto & el : m_elems) @@ -133,7 +133,7 @@ namespace lp { lm.m_head = m_line.substr(0, separator); m_line = m_line.substr(lm.m_head.size()); eat_blanks(); - while (m_line.size()) { + while (!m_line.empty()) { if (m_line[0] == '(') { lisp_elem el; fill_nested_elem(el); @@ -152,7 +152,7 @@ namespace lp { } void eat_blanks() { - while (m_line.size()) { + while (!m_line.empty()) { if (m_line[0] == ' ') m_line = m_line.substr(1); else @@ -193,19 +193,19 @@ namespace lp { } } - void adjust_rigth_side(formula_constraint & /* c*/, lisp_elem & /*el*/) { + void adjust_right_side(formula_constraint & /* c*/, lisp_elem & /*el*/) { // lp_assert(el.m_head == "0"); // do nothing for the time being } void set_constraint_coeffs(formula_constraint & c, lisp_elem & el) { lp_assert(el.m_elems.size() == 2); set_constraint_coeffs_on_coeff_element(c, el.m_elems[0]); - adjust_rigth_side(c, el.m_elems[1]); + adjust_right_side(c, el.m_elems[1]); } bool is_integer(std::string & s) { - if (s.size() == 0) return false; + if (s.empty()) return false; return atoi(s.c_str()) != 0 || isdigit(s.c_str()[0]); } diff --git a/src/test/main.cpp b/src/test/main.cpp index 811b67407..4aeee185b 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -143,7 +143,6 @@ int main(int argc, char ** argv) { bool test_all = false; parse_cmd_line_args(argc, argv, do_display_usage, test_all); TST(random); - TST(vector); TST(symbol_table); TST(region); TST(symbol); @@ -213,6 +212,7 @@ int main(int argc, char ** argv) { if (test_all) return 0; TST(ext_numeral); TST(interval); + TST(vector); TST(f2n); TST(hwf); TST(trigo); diff --git a/src/test/matcher.cpp b/src/test/matcher.cpp index cd220a9c6..68f37ccb6 100644 --- a/src/test/matcher.cpp +++ b/src/test/matcher.cpp @@ -84,7 +84,7 @@ void tst1() { app_ref b( m.mk_const(symbol("b"), s), m); expr_ref x( m.mk_var(0, s), m); expr_ref y( m.mk_var(1, s), m); - app_ref gx( m.mk_app(g, x), m); + app_ref gx( m.mk_app(g.get(), x.get()), m); app_ref fgx_x( m.mk_app(f, gx.get(), x.get()), m); app_ref ha( m.mk_app(h, a.get()), m); app_ref gha( m.mk_app(g, ha.get()), m); diff --git a/src/test/sat_local_search.cpp b/src/test/sat_local_search.cpp index ad982d024..46d96d637 100644 --- a/src/test/sat_local_search.cpp +++ b/src/test/sat_local_search.cpp @@ -47,9 +47,6 @@ static bool build_instance(char const * filename, sat::solver& s, sat::local_sea return false; } - for (unsigned i = 0; i < lits.size(); ++i) { - local_search.add_soft(lits[i].var(), coefficients[i]); - } // read the constraints, one at a time int k; diff --git a/src/test/theory_pb.cpp b/src/test/theory_pb.cpp index 7010b96d0..bff5c3809 100644 --- a/src/test/theory_pb.cpp +++ b/src/test/theory_pb.cpp @@ -11,7 +11,7 @@ Copyright (c) 2015 Microsoft Corporation #include "smt/theory_pb.h" #include "ast/rewriter/th_rewriter.h" -unsigned populate_literals(unsigned k, smt::literal_vector& lits) { +static unsigned populate_literals(unsigned k, smt::literal_vector& lits) { ENSURE(k < (1u << lits.size())); unsigned t = 0; for (unsigned i = 0; i < lits.size(); ++i) { @@ -36,7 +36,6 @@ class pb_fuzzer { public: pb_fuzzer(ast_manager& m): m(m), rand(0), ctx(m, params), vars(m) { params.m_model = true; - params.m_pb_enable_simplex = true; unsigned N = 3; for (unsigned i = 0; i < N; ++i) { std::stringstream strm; @@ -48,7 +47,6 @@ public: void fuzz() { enable_trace("pb"); - enable_trace("simplex"); unsigned nr = 0; for (unsigned i = 0; i < 100000; ++i) { fuzz_round(nr, 2); @@ -84,6 +82,7 @@ private: } std::cout << "(assert " << fml << ")\n"; ctx.assert_expr(fml); + std::cout << ";asserted\n"; } @@ -138,11 +137,8 @@ void tst_theory_pb() { unsigned k = populate_literals(i, lits); std::cout << "k:" << k << " " << N << "\n"; std::cout.flush(); - TRACE("pb", tout << "k " << k << ": "; - for (unsigned j = 0; j < lits.size(); ++j) { - tout << lits[j] << " "; - } - tout << "\n";); + TRACE("pb", tout << "k " << k << ": " << lits << "\n";); + { smt::context ctx(m, params); ctx.push(); diff --git a/src/test/upolynomial.cpp b/src/test/upolynomial.cpp index fa106fe3f..f8500dfa9 100644 --- a/src/test/upolynomial.cpp +++ b/src/test/upolynomial.cpp @@ -891,6 +891,7 @@ static void tst_fact(polynomial_ref const & p, unsigned num_distinct_factors, up for (unsigned i = 0; i < fs.distinct_factors(); i++) { std::cout << "*("; um.display(std::cout, fs[i]); std::cout << ")^" << fs.get_degree(i) << std::endl; } + std::cout << fs.distinct_factors() << " " << num_distinct_factors << "\n"; ENSURE(fs.distinct_factors() == num_distinct_factors); upolynomial::scoped_numeral_vector _r(um); fs.multiply(_r); @@ -906,10 +907,10 @@ static void tst_fact() { x0 = m.mk_polynomial(m.mk_var()); tst_fact((x0^4) + (x0^2) - 20, 3); tst_fact((x0^4) + (x0^2) - 20, 1, upolynomial::factor_params(5, 1, 1000)); - tst_fact((x0^4) + (x0^2) - 20, 3, upolynomial::factor_params(7, 1, 1000)); + tst_fact((x0^4) + (x0^2) - 20, 1, upolynomial::factor_params(7, 1, 1000)); tst_fact((x0^70) - 6*(x0^65) - (x0^60) + 60*(x0^55) - 54*(x0^50) - 230*(x0^45) + 274*(x0^40) + 542*(x0^35) - 615*(x0^30) - 1120*(x0^25) + 1500*(x0^20) - 160*(x0^15) - 395*(x0^10) + 76*(x0^5) + 34, 1, upolynomial::factor_params(3, 1, 20)); - tst_fact((x0^70) - 6*(x0^65) - (x0^60) + 60*(x0^55) - 54*(x0^50) - 230*(x0^45) + 274*(x0^40) + 542*(x0^35) - 615*(x0^30) - 1120*(x0^25) + 1500*(x0^20) - 160*(x0^15) - 395*(x0^10) + 76*(x0^5) + 34, 2, upolynomial::factor_params(3, 1, 72)); - tst_fact((x0^70) - 6*(x0^65) - (x0^60) + 60*(x0^55) - 54*(x0^50) - 230*(x0^45) + 274*(x0^40) + 542*(x0^35) - 615*(x0^30) - 1120*(x0^25) + 1500*(x0^20) - 160*(x0^15) - 395*(x0^10) + 76*(x0^5) + 34, 3, upolynomial::factor_params(3, 1, 80)); + tst_fact((x0^70) - 6*(x0^65) - (x0^60) + 60*(x0^55) - 54*(x0^50) - 230*(x0^45) + 274*(x0^40) + 542*(x0^35) - 615*(x0^30) - 1120*(x0^25) + 1500*(x0^20) - 160*(x0^15) - 395*(x0^10) + 76*(x0^5) + 34, 1, upolynomial::factor_params(3, 1, 72)); + tst_fact((x0^70) - 6*(x0^65) - (x0^60) + 60*(x0^55) - 54*(x0^50) - 230*(x0^45) + 274*(x0^40) + 542*(x0^35) - 615*(x0^30) - 1120*(x0^25) + 1500*(x0^20) - 160*(x0^15) - 395*(x0^10) + 76*(x0^5) + 34, 1, upolynomial::factor_params(3, 1, 80)); tst_fact( (x0^10) - 10*(x0^8) + 38*(x0^6) - 2*(x0^5) - 100*(x0^4) - 40*(x0^3) + 121*(x0^2) - 38*x0 - 17, 1); tst_fact( (x0^4) - 404*(x0^2) + 39204, 2); tst_fact(((x0^5) - (x0^2) + 1)*((-1)*x0 + 1)*((x0^2) - 2*x0 + 3), 3); diff --git a/src/test/vector.cpp b/src/test/vector.cpp index 4632a9c29..c9a93dee4 100644 --- a/src/test/vector.cpp +++ b/src/test/vector.cpp @@ -41,7 +41,7 @@ static void tst1() { v1.pop_back(); } ENSURE(v1.empty()); - ENSURE(v1.size() == 0); + ENSURE(v1.empty()); unsigned i = 1000000000; while (true) { std::cout << "resize " << i << "\n"; diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index b6abb785f..5b1f336d1 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -56,7 +56,6 @@ z3_add_component(util symbol.cpp timeit.cpp timeout.cpp - timer.cpp trace.cpp util.cpp warning.cpp diff --git a/src/util/array.h b/src/util/array.h index cf82e123c..9f0321777 100644 --- a/src/util/array.h +++ b/src/util/array.h @@ -154,7 +154,7 @@ public: return static_cast(reinterpret_cast(m_data)[SIZE_IDX]); } - bool empty() const { return m_data == 0; } + bool empty() const { return m_data == nullptr; } T & operator[](unsigned idx) { SASSERT(idx < size()); diff --git a/src/util/chashtable.h b/src/util/chashtable.h index e49ac3bd4..19ff1ef51 100644 --- a/src/util/chashtable.h +++ b/src/util/chashtable.h @@ -32,6 +32,7 @@ Revision History: #include "util/debug.h" #include "util/trace.h" #include "util/tptr.h" +#include "util/util.h" #ifdef Z3DEBUG #include "util/hashtable.h" #endif diff --git a/src/util/debug.cpp b/src/util/debug.cpp index 4434cb19f..1b85fbbbb 100644 --- a/src/util/debug.cpp +++ b/src/util/debug.cpp @@ -56,20 +56,20 @@ void finalize_debug() { void enable_debug(const char * tag) { init_debug_table(); - g_enabled_debug_tags->insert(const_cast(tag)); + g_enabled_debug_tags->insert(tag); } void disable_debug(const char * tag) { init_debug_table(); - g_enabled_debug_tags->erase(const_cast(tag)); + g_enabled_debug_tags->erase(tag); } bool is_debug_enabled(const char * tag) { init_debug_table(); - return g_enabled_debug_tags->contains(const_cast(tag)); + return g_enabled_debug_tags->contains(tag); } -#ifndef _WINDOWS +#if !defined(_WINDOWS) && !defined(NO_Z3_DEBUGGER) void invoke_gdb() { char buffer[1024]; int * x = nullptr; diff --git a/src/util/debug.h b/src/util/debug.h index 536df4588..613328013 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -44,6 +44,17 @@ bool assertions_enabled(); #define DEBUG_CODE(CODE) ((void) 0) #endif +#ifdef __APPLE__ +#include +#if !TARGET_OS_OSX +#define NO_Z3_DEBUGGER +#endif +#endif + +#ifdef __EMSCRIPTEN__ +#define NO_Z3_DEBUGGER +#endif + #ifdef NO_Z3_DEBUGGER #define INVOKE_DEBUGGER() exit(ERR_INTERNAL_FATAL) #else diff --git a/src/util/ema.h b/src/util/ema.h index 5a32e021c..7391d8ea0 100644 --- a/src/util/ema.h +++ b/src/util/ema.h @@ -51,6 +51,10 @@ class ema { m_beta *= 0.5; if (m_beta < m_alpha) m_beta = m_alpha; } + + void set(double x) { + m_value = x; + } }; #endif diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index 32b1343e3..4a7a0b7e4 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -61,7 +61,7 @@ hwf_manager::hwf_manager() : m_mpz_manager(m_mpq_manager) { #ifdef _WINDOWS -#if defined(_AMD64_) || defined(_M_IA64) +#if defined(_WIN64) // Precision control is not supported on x64. // See: http://msdn.microsoft.com/en-us/library/e9b52ceh(VS.110).aspx // CMW: I think this is okay though, the compiler will chose the right instructions @@ -303,7 +303,7 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o // CMW: modf is not the right function here. // modf(x.value, &o.value); - // According to the Intel Architecture manual, the x87-instrunction FRNDINT is the + // According to the Intel Architecture manual, the x87-instruction FRNDINT is the // same in 32-bit and 64-bit mode. The _mm_round_* intrinsics are SSE4 extensions. #ifdef _WINDOWS #if defined(USE_INTRINSICS) && \ @@ -557,7 +557,7 @@ void hwf_manager::mk_ninf(hwf & o) { } #ifdef _WINDOWS -#if defined(_AMD64_) || defined(_M_IA64) +#if defined(_WIN64) #ifdef USE_INTRINSICS #define SETRM(RM) _MM_SET_ROUNDING_MODE(RM) #else diff --git a/src/util/lp/core_solver_pretty_printer_def.h b/src/util/lp/core_solver_pretty_printer_def.h index dcf3e0a11..5453c6085 100644 --- a/src/util/lp/core_solver_pretty_printer_def.h +++ b/src/util/lp/core_solver_pretty_printer_def.h @@ -183,7 +183,7 @@ template unsigned core_solver_pretty_printer:: ge } if (!m_core_solver.use_tableau()) { w = std::max(w, (unsigned)T_to_string(m_exact_column_norms[column]).size()); - if (m_core_solver.m_column_norms.size() > 0) + if (!m_core_solver.m_column_norms.empty()) w = std::max(w, (unsigned)T_to_string(m_core_solver.m_column_norms[column]).size()); } return w; @@ -339,7 +339,7 @@ template void core_solver_pretty_printer::print() print_lows(); print_upps(); print_exact_norms(); - if (m_core_solver.m_column_norms.size() > 0) + if (!m_core_solver.m_column_norms.empty()) print_approx_norms(); m_out << std::endl; } diff --git a/src/util/lp/eta_matrix_def.h b/src/util/lp/eta_matrix_def.h index 5c7661e24..774cc89dc 100644 --- a/src/util/lp/eta_matrix_def.h +++ b/src/util/lp/eta_matrix_def.h @@ -81,7 +81,7 @@ void eta_matrix::apply_from_right(vector & w) { } template void eta_matrix::apply_from_right(indexed_vector & w) { - if (w.m_index.size() == 0) + if (w.m_index.empty()) return; #ifdef Z3DEBUG // vector wcopy(w.m_data); diff --git a/src/util/lp/general_matrix.h b/src/util/lp/general_matrix.h index 1c643161a..f6f93c705 100644 --- a/src/util/lp/general_matrix.h +++ b/src/util/lp/general_matrix.h @@ -51,7 +51,7 @@ public: unsigned row_count() const { return m_data.size(); } - unsigned column_count() const { return m_data.size() > 0? m_data[0].size() : 0; } + unsigned column_count() const { return !m_data.empty()? m_data[0].size() : 0; } class ref_row { general_matrix& m_matrix; diff --git a/src/util/lp/lar_term.h b/src/util/lp/lar_term.h index e9259b8c0..64243f4e2 100644 --- a/src/util/lp/lar_term.h +++ b/src/util/lp/lar_term.h @@ -37,7 +37,7 @@ struct lar_term { } bool is_empty() const { - return m_coeffs.size() == 0; // && is_zero(m_v); + return m_coeffs.empty(); // && is_zero(m_v); } unsigned size() const { return static_cast(m_coeffs.size()); } diff --git a/src/util/lp/lp_dual_core_solver_def.h b/src/util/lp/lp_dual_core_solver_def.h index e7ab73928..984f2e560 100644 --- a/src/util/lp/lp_dual_core_solver_def.h +++ b/src/util/lp/lp_dual_core_solver_def.h @@ -665,7 +665,7 @@ template bool lp_dual_core_solver::ratio_test() { m_flipped_boxed.clear(); int initial_delta_sign = m_delta >= numeric_traits::zero()? 1: -1; do { - if (m_breakpoint_set.size() == 0) { + if (m_breakpoint_set.empty()) { set_status_to_tentative_dual_unbounded_or_dual_unbounded(); return false; } @@ -697,7 +697,7 @@ template void lp_dual_core_solver::update_d_and_x this->m_d[j] -= m_theta_D * this->m_pivot_row[j]; } this->m_d[m_p] = - m_theta_D; - if (m_flipped_boxed.size() > 0) { + if (!m_flipped_boxed.empty()) { process_flipped(); update_xb_after_bound_flips(); } diff --git a/src/util/lp/lp_primal_core_solver_def.h b/src/util/lp/lp_primal_core_solver_def.h index d86ebf548..a43764172 100644 --- a/src/util/lp/lp_primal_core_solver_def.h +++ b/src/util/lp/lp_primal_core_solver_def.h @@ -504,7 +504,7 @@ lp_primal_core_solver::get_bound_on_variable_and_update_leaving_precisely( X tt = - (this->m_lower_bounds[j] - this->m_x[j]) / m; if (numeric_traits::is_neg(tt)) tt = zero_of_type(); - if (leavings.size() == 0 || tt < t || (tt == t && m > abs_of_d_of_leaving)) { + if (leavings.empty() || tt < t || (tt == t && m > abs_of_d_of_leaving)) { t = tt; abs_of_d_of_leaving = m; leavings.clear(); @@ -524,7 +524,7 @@ lp_primal_core_solver::get_bound_on_variable_and_update_leaving_precisely( X tt = (this->m_upper_bounds[j] - this->m_x[j]) / m; if (numeric_traits::is_neg(tt)) tt = zero_of_type(); - if (leavings.size() == 0 || tt < t || (tt == t && - m > abs_of_d_of_leaving)) { + if (leavings.empty() || tt < t || (tt == t && - m > abs_of_d_of_leaving)) { t = tt; abs_of_d_of_leaving = - m; leavings.clear(); diff --git a/src/util/lp/lp_solver_def.h b/src/util/lp/lp_solver_def.h index 9b385dee6..21a86efba 100644 --- a/src/util/lp/lp_solver_def.h +++ b/src/util/lp/lp_solver_def.h @@ -103,7 +103,7 @@ template void lp_solver::flip_costs() { template bool lp_solver::problem_is_empty() { for (auto & c : m_A_values) - if (c.second.size()) + if (!c.second.empty()) return false; return true; } @@ -387,7 +387,7 @@ template unsigned lp_solver::try_to_remove_some_r return 0; } } - if (rows_to_delete.size() > 0) { + if (!rows_to_delete.empty()) { for (unsigned k : rows_to_delete) { m_A_values.erase(k); } diff --git a/src/util/lp/matrix_def.h b/src/util/lp/matrix_def.h index 361540cae..b74e59443 100644 --- a/src/util/lp/matrix_def.h +++ b/src/util/lp/matrix_def.h @@ -97,7 +97,7 @@ void print_matrix_with_widths(vector> & A, vector void print_string_matrix(vector> & A, std::ostream & out, unsigned blanks_in_front) { vector widths; - if (A.size() > 0) + if (!A.empty()) for (unsigned j = 0; j < A[0].size(); j++) { widths.push_back(get_width_of_column(j, A)); } diff --git a/src/util/lp/mps_reader.h b/src/util/lp/mps_reader.h index 09762cd5e..2ef07af6e 100644 --- a/src/util/lp/mps_reader.h +++ b/src/util/lp/mps_reader.h @@ -220,7 +220,7 @@ class mps_reader { *m_message_stream << "cannot read from file" << std::endl; } m_line_number++; - if (m_line.size() != 0 && m_line[0] != '*' && !all_white_space()) + if (!m_line.empty() && m_line[0] != '*' && !all_white_space()) break; } } @@ -514,7 +514,7 @@ class mps_reader { lp_assert(m_line.size() >= 14); vector bound_string = split_and_trim(m_line.substr(name_offset, m_line.size())); - if (bound_string.size() == 0) { + if (bound_string.empty()) { set_m_ok_to_false(); (*m_message_stream) << "error at line " << m_line_number << std::endl; throw m_line; diff --git a/src/util/lp/square_sparse_matrix_def.h b/src/util/lp/square_sparse_matrix_def.h index cc6625453..2fc101666 100644 --- a/src/util/lp/square_sparse_matrix_def.h +++ b/src/util/lp/square_sparse_matrix_def.h @@ -248,7 +248,7 @@ void square_sparse_matrix::put_max_index_to_0(vector> & r template void square_sparse_matrix::set_max_in_row(vector> & row_vals) { - if (row_vals.size() == 0) + if (row_vals.empty()) return; T max_val = abs(row_vals[0].m_value); unsigned max_index = 0; @@ -386,7 +386,7 @@ bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_ } work_vec.m_index.clear(); auto & row_vals = m_rows[i0]; - if (row_vals.size() == 0) { + if (row_vals.empty()) { return false; } set_max_in_row(row_vals); // it helps to find larger pivots diff --git a/src/util/machine.h b/src/util/machine.h index 70baee41e..1ccff6330 100644 --- a/src/util/machine.h +++ b/src/util/machine.h @@ -20,7 +20,7 @@ Revision History: #ifndef MACHINE_H_ #define MACHINE_H_ -#ifdef _AMD64_ +#if defined(__LP64__) || defined(_WIN64) #define PTR_ALIGNMENT 3 #else #define PTR_ALIGNMENT 2 diff --git a/src/util/memory_manager.cpp b/src/util/memory_manager.cpp index 148306fbd..6ef94e880 100644 --- a/src/util/memory_manager.cpp +++ b/src/util/memory_manager.cpp @@ -18,7 +18,7 @@ Copyright (c) 2015 Microsoft Corporation // ADD_INITIALIZER('rational::initialize();') // ADD_FINALIZER('rational::finalize();') // Thus, any executable or shared object (DLL) that depends on rational.h -// will have an automalically generated file mem_initializer.cpp containing +// will have an automatically generated file mem_initializer.cpp containing // mem_initialize() // mem_finalize() // and these functions will include the statements: @@ -242,13 +242,13 @@ void * memory::allocate(char const* file, int line, char const* obj, size_t s) { #define SYNCH_THRESHOLD 100000 #ifdef _WINDOWS -// Actually this is VS specific instead of Windows specific. +// This is VS2013 specific instead of Windows specific. +// It can go away with VS2017 builds __declspec(thread) long long g_memory_thread_alloc_size = 0; __declspec(thread) long long g_memory_thread_alloc_count = 0; #else -// GCC style -__thread long long g_memory_thread_alloc_size = 0; -__thread long long g_memory_thread_alloc_count = 0; +thread_local long long g_memory_thread_alloc_size = 0; +thread_local long long g_memory_thread_alloc_count = 0; #endif static void synchronize_counters(bool allocating) { diff --git a/src/util/mpn.cpp b/src/util/mpn.cpp index b8cab4a9f..25445bc89 100644 --- a/src/util/mpn.cpp +++ b/src/util/mpn.cpp @@ -381,7 +381,7 @@ char * mpn_manager::to_string(mpn_digit const * a, size_t const lng, char * buf, div_1(t_numer, t_denom[0], &temp[0]); div_unnormalize(t_numer, t_denom, d, &rem); buf[j++] = '0' + rem; - while (temp.size() > 0 && temp.back() == 0) + while (!temp.empty() && temp.back() == 0) temp.pop_back(); } buf[j] = 0; diff --git a/src/util/mpn.h b/src/util/mpn.h index a5d3c30d8..bae972b0c 100644 --- a/src/util/mpn.h +++ b/src/util/mpn.h @@ -37,31 +37,31 @@ public: mpn_manager(); ~mpn_manager(); - int compare(mpn_digit const * a, size_t const lnga, - mpn_digit const * b, size_t const lngb) const; + int compare(mpn_digit const * a, size_t lnga, + mpn_digit const * b, size_t lngb) const; - bool add(mpn_digit const * a, size_t const lnga, - mpn_digit const * b, size_t const lngb, - mpn_digit *c, size_t const lngc_alloc, + bool add(mpn_digit const * a, size_t lnga, + mpn_digit const * b, size_t lngb, + mpn_digit *c, size_t lngc_alloc, size_t * plngc) const; - bool sub(mpn_digit const * a, size_t const lnga, - mpn_digit const * b, size_t const lngb, + bool sub(mpn_digit const * a, size_t lnga, + mpn_digit const * b, size_t lngb, mpn_digit * c, mpn_digit * pborrow) const; - bool mul(mpn_digit const * a, size_t const lnga, - mpn_digit const * b, size_t const lngb, + bool mul(mpn_digit const * a, size_t lnga, + mpn_digit const * b, size_t lngb, mpn_digit * c) const; - bool div(mpn_digit const * numer, size_t const lnum, - mpn_digit const * denom, size_t const lden, + bool div(mpn_digit const * numer, size_t lnum, + mpn_digit const * denom, size_t lden, mpn_digit * quot, mpn_digit * rem); - char * to_string(mpn_digit const * a, size_t const lng, - char * buf, size_t const lbuf) const; + char * to_string(mpn_digit const * a, size_t lng, + char * buf, size_t lbuf) const; private: - #ifdef _AMD64_ + #if defined(__LP64__) || defined(_WIN64) class mpn_sbuffer : public sbuffer { public: mpn_sbuffer() : sbuffer() {} @@ -88,29 +88,29 @@ private: static const mpn_digit zero; mpn_sbuffer u, v, t_ms, t_ab; - void display_raw(std::ostream & out, mpn_digit const * a, size_t const lng) const; + void display_raw(std::ostream & out, mpn_digit const * a, size_t lng) const; - size_t div_normalize(mpn_digit const * numer, size_t const lnum, - mpn_digit const * denom, size_t const lden, + size_t div_normalize(mpn_digit const * numer, size_t lnum, + mpn_digit const * denom, size_t lden, mpn_sbuffer & n_numer, mpn_sbuffer & n_denom) const; void div_unnormalize(mpn_sbuffer & numer, mpn_sbuffer & denom, - size_t const d, mpn_digit * rem) const; + size_t d, mpn_digit * rem) const; - bool div_1(mpn_sbuffer & numer, mpn_digit const denom, + bool div_1(mpn_sbuffer & numer, mpn_digit denom, mpn_digit * quot) const; bool div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom, mpn_digit * quot, mpn_digit * rem, mpn_sbuffer & ms, mpn_sbuffer & ab) const; - void trace(mpn_digit const * a, size_t const lnga, - mpn_digit const * b, size_t const lngb, + void trace(mpn_digit const * a, size_t lnga, + mpn_digit const * b, size_t lngb, const char * op) const; - void trace(mpn_digit const * a, size_t const lnga) const; - void trace_nl(mpn_digit const * a, size_t const lnga) const; + void trace(mpn_digit const * a, size_t lnga) const; + void trace_nl(mpn_digit const * a, size_t lnga) const; }; #endif diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 32a074eb3..0d3a6040d 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -30,7 +30,6 @@ Revision History: #else #error No multi-precision library selected. #endif -#include // Available GCD algorithms // #define EUCLID_GCD @@ -46,13 +45,18 @@ Revision History: #define LEHMER_GCD #endif +#ifdef _WINDOWS +// This is needed for _tzcnt_u32 and friends. +#include +#endif + #if defined(__GNUC__) #define _trailing_zeros32(X) __builtin_ctz(X) #else #define _trailing_zeros32(X) _tzcnt_u32(X) #endif -#if defined(_AMD64_) +#if defined(__LP64__) || defined(_WIN64) #if defined(__GNUC__) #define _trailing_zeros64(X) __builtin_ctzll(X) #else diff --git a/src/util/obj_ref.h b/src/util/obj_ref.h index 8908f2cdd..e3b0d0710 100644 --- a/src/util/obj_ref.h +++ b/src/util/obj_ref.h @@ -94,10 +94,8 @@ public: obj_ref & operator=(obj_ref && n) { SASSERT(&m_manager == &n.m_manager); - if (this != &n) { - std::swap(m_obj, n.m_obj); - n.reset(); - } + std::swap(m_obj, n.m_obj); + n.reset(); return *this; } diff --git a/src/util/params.cpp b/src/util/params.cpp index 43f53514a..bfb6095d6 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -298,9 +298,14 @@ void insert_produce_proofs(param_descrs & r) { } void insert_timeout(param_descrs & r) { - r.insert("timeout", CPK_UINT, "(default: infty) timeout in milliseconds."); + r.insert("timeout", CPK_UINT, "(default: infty) timeout in milliseconds.", "4294967295"); } +void insert_rlimit(param_descrs & r) { + r.insert("rlimit", CPK_UINT, "default resource limit used for solvers. Unrestricted when set to 0.", "0"); +} + + class params { friend class params_ref; struct value { diff --git a/src/util/params.h b/src/util/params.h index 0a68a9606..5330993c1 100644 --- a/src/util/params.h +++ b/src/util/params.h @@ -139,5 +139,6 @@ void insert_max_steps(param_descrs & r); void insert_produce_models(param_descrs & r); void insert_produce_proofs(param_descrs & r); void insert_timeout(param_descrs & r); +void insert_rlimit(param_descrs & r); #endif diff --git a/src/util/ref_buffer.h b/src/util/ref_buffer.h index 4768c3f28..49213cc62 100644 --- a/src/util/ref_buffer.h +++ b/src/util/ref_buffer.h @@ -58,6 +58,12 @@ public: inc_ref(n); m_buffer.push_back(n); } + + template + void push_back(obj_ref && n) { + m_buffer.push_back(n.get()); + n.steal(); + } void pop_back() { SASSERT(!m_buffer.empty()); diff --git a/src/util/ref_vector.h b/src/util/ref_vector.h index 02803a514..732ec20dd 100644 --- a/src/util/ref_vector.h +++ b/src/util/ref_vector.h @@ -99,8 +99,8 @@ public: return *this; } - template - ref_vector_core& push_back(obj_ref && n) { + template + ref_vector_core& push_back(obj_ref && n) { m_nodes.push_back(n.get()); n.steal(); return *this; @@ -306,6 +306,18 @@ public: // prevent abuse: ref_vector & operator=(ref_vector const & other) = delete; + bool operator==(ref_vector const& other) const { + if (other.size() != this->size()) return false; + for (unsigned i = this->size(); i-- > 0; ) { + if (other[i] != (*this)[i]) return false; + } + return true; + } + + bool operator!=(ref_vector const& other) const { + return !(*this == other); + } + bool forall(std::function& predicate) const { for (T* t : *this) if (!predicate(t)) diff --git a/src/util/scoped_timer.cpp b/src/util/scoped_timer.cpp index 9850699be..58416289a 100644 --- a/src/util/scoped_timer.cpp +++ b/src/util/scoped_timer.cpp @@ -16,240 +16,43 @@ Author: Revision History: --*/ -#ifdef _CYGWIN -// Hack to make CreateTimerQueueTimer available on cygwin -#define _WIN32_WINNT 0x0600 -#endif - -#include "util/z3_exception.h" -#include "util/z3_omp.h" -#if defined(_WINDOWS) || defined(_CYGWIN) -// Windows -#include -#elif defined(__APPLE__) && defined(__MACH__) -// macOS -#include -#include -#include -#include -#include -#elif defined(_LINUX_) || defined(_FREEBSD_) || defined(_NetBSD_) -// Linux & FreeBSD & NetBSD -#include -#include -#include -#include -// --------- -#else -// Other platforms -#endif #include "util/scoped_timer.h" -#ifdef _CYGWIN -#undef min -#undef max -#endif #include "util/util.h" -#include -#include "util/z3_omp.h" +#include +#include +#include +#include + struct scoped_timer::imp { - event_handler * m_eh; -#if defined(_WINDOWS) || defined(_CYGWIN) - HANDLE m_timer; - bool m_first; -#elif defined(__APPLE__) && defined(__MACH__) - // macOS - pthread_t m_thread_id; - pthread_attr_t m_attributes; - unsigned m_interval; - pthread_mutex_t m_mutex; - pthread_cond_t m_condition_var; - struct timespec m_end_time; -#elif defined(_LINUX_) || defined(_FREEBSD_) || defined(_NETBSD_) - // Linux & FreeBSD & NetBSD - pthread_t m_thread_id; - pthread_mutex_t m_mutex; - pthread_cond_t m_cond; - unsigned m_ms; - bool m_initialized; - bool m_signal_sent; -#else - // Other -#endif +private: + std::thread m_thread; + std::timed_mutex m_mutex; -#if defined(_WINDOWS) || defined(_CYGWIN) - static void CALLBACK abort_proc(PVOID param, BOOLEAN timer_or_wait_fired) { - imp * obj = static_cast(param); - if (obj->m_first) { - obj->m_first = false; - } - else { - obj->m_eh->operator()(TIMEOUT_EH_CALLER); + static void thread_func(unsigned ms, event_handler * eh, std::timed_mutex * mutex) { + auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); + + while (!mutex->try_lock_until(end)) { + if (std::chrono::steady_clock::now() >= end) { + eh->operator()(TIMEOUT_EH_CALLER); + return; + } } + + mutex->unlock(); } -#elif defined(__APPLE__) && defined(__MACH__) - // macOS - static void * thread_func(void * arg) { - scoped_timer::imp * st = static_cast(arg); - pthread_mutex_lock(&st->m_mutex); - - int e = pthread_cond_timedwait(&st->m_condition_var, &st->m_mutex, &st->m_end_time); - if (e != 0 && e != ETIMEDOUT) - throw default_exception("failed to start timed wait"); - st->m_eh->operator()(TIMEOUT_EH_CALLER); - - pthread_mutex_unlock(&st->m_mutex); - - return st; - } -#elif defined(_LINUX_) || defined(_FREEBSD_) || defined(_NETBSD_) - static void* thread_func(void *arg) { - scoped_timer::imp *st = static_cast(arg); - - struct timespec end_time; - clock_gettime(CLOCK_REALTIME, &end_time); - end_time.tv_sec += st->m_ms / 1000u; - end_time.tv_nsec += (st->m_ms % 1000u) * 1000000ull; - // check for overflow - if (end_time.tv_nsec >= 1000000000) { - ++end_time.tv_sec; - end_time.tv_nsec -= 1000000000; - } - - pthread_mutex_lock(&st->m_mutex); - st->m_initialized = true; - int e = 0; - // `pthread_cond_timedwait()` may spuriously wake even if the signal - // was not sent so we loop until a timeout occurs or the signal was - // **really** sent. - while (!(e == 0 && st->m_signal_sent)) { - e = pthread_cond_timedwait(&st->m_cond, &st->m_mutex, &end_time); - ENSURE(e == 0 || e == ETIMEDOUT); - if (e == ETIMEDOUT) - break; - } - pthread_mutex_unlock(&st->m_mutex); - - if (e == ETIMEDOUT) - st->m_eh->operator()(TIMEOUT_EH_CALLER); - return 0; - } -#else - // Other -#endif - - - imp(unsigned ms, event_handler * eh): - m_eh(eh) { -#if defined(_WINDOWS) || defined(_CYGWIN) - m_first = true; - CreateTimerQueueTimer(&m_timer, - NULL, - abort_proc, - this, - 0, - ms, - WT_EXECUTEINTIMERTHREAD); -#elif defined(__APPLE__) && defined(__MACH__) - // macOS - m_interval = ms?ms:0xFFFFFFFF; - if (pthread_attr_init(&m_attributes) != 0) - throw default_exception("failed to initialize timer thread attributes"); - if (pthread_cond_init(&m_condition_var, nullptr) != 0) - throw default_exception("failed to initialize timer condition variable"); - if (pthread_mutex_init(&m_mutex, nullptr) != 0) - throw default_exception("failed to initialize timer mutex"); - - clock_serv_t host_clock; - mach_timespec_t now; - unsigned long long nano = static_cast(m_interval) * 1000000ull; - - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &host_clock); - m_end_time.tv_sec = nano / 1000000000ull; - m_end_time.tv_nsec = nano % 1000000000ull; - clock_get_time(host_clock, &now); - ADD_MACH_TIMESPEC(&m_end_time, &now); - - - if (pthread_create(&m_thread_id, &m_attributes, &thread_func, this) != 0) - throw default_exception("failed to start timer thread"); -#elif defined(_LINUX_) || defined(_FREEBSD_) || defined(_NETBSD_) - // Linux & FreeBSD & NetBSD - m_ms = ms; - m_initialized = false; - m_signal_sent = false; - ENSURE(pthread_mutex_init(&m_mutex, NULL) == 0); - ENSURE(pthread_cond_init(&m_cond, NULL) == 0); - ENSURE(pthread_create(&m_thread_id, NULL, &thread_func, this) == 0); -#else - // Other platforms -#endif +public: + imp(unsigned ms, event_handler * eh) { + m_mutex.lock(); + m_thread = std::thread(thread_func, ms, eh, &m_mutex); } ~imp() { -#if defined(_WINDOWS) || defined(_CYGWIN) - DeleteTimerQueueTimer(NULL, - m_timer, - INVALID_HANDLE_VALUE); -#elif defined(__APPLE__) && defined(__MACH__) - // macOS - - // If the waiting-thread is not up and waiting yet, - // we can make sure that it finishes quickly by - // setting the end-time to zero. - m_end_time.tv_sec = 0; - m_end_time.tv_nsec = 0; - - // Otherwise it's already up and waiting, and - // we can send a signal on m_condition_var: - pthread_mutex_lock(&m_mutex); - pthread_cond_signal(&m_condition_var); - pthread_mutex_unlock(&m_mutex); - - if (pthread_join(m_thread_id, nullptr) != 0) { - warning_msg("failed to join thread"); - return; - } - if (pthread_mutex_destroy(&m_mutex) != 0) { - warning_msg("failed to destroy pthread mutex"); - return; - } - if (pthread_cond_destroy(&m_condition_var) != 0) { - warning_msg("failed to destroy pthread condition variable"); - return; - } - if (pthread_attr_destroy(&m_attributes) != 0) { - warning_msg("failed to destroy pthread attributes object"); - return; - } -#elif defined(_LINUX_) || defined(_FREEBSD_) || defined(_NETBSD_) - // Linux & FreeBSD & NetBSD - bool init = false; - - // spin until timer thread has been created - while (!init) { - pthread_mutex_lock(&m_mutex); - init = m_initialized; - pthread_mutex_unlock(&m_mutex); - if (!init) - sched_yield(); - } - pthread_mutex_lock(&m_mutex); - m_signal_sent = true; - pthread_mutex_unlock(&m_mutex); - // Perform signal outside of lock to avoid waking timing thread twice. - pthread_cond_signal(&m_cond); - - pthread_join(m_thread_id, NULL); - pthread_cond_destroy(&m_cond); - pthread_mutex_destroy(&m_mutex); -#else - // Other Platforms -#endif + m_mutex.unlock(); + m_thread.join(); } - }; scoped_timer::scoped_timer(unsigned ms, event_handler * eh) { @@ -260,6 +63,5 @@ scoped_timer::scoped_timer(unsigned ms, event_handler * eh) { } scoped_timer::~scoped_timer() { - if (m_imp) - dealloc(m_imp); + dealloc(m_imp); } diff --git a/src/util/stopwatch.h b/src/util/stopwatch.h index a11a87484..1135c893e 100644 --- a/src/util/stopwatch.h +++ b/src/util/stopwatch.h @@ -20,171 +20,60 @@ Revision History: #ifndef STOPWATCH_H_ #define STOPWATCH_H_ -#if defined(_WINDOWS) || defined(_CYGWIN) || defined(_MINGW) - -// Does this redefinition work? - -#include +#include "util/debug.h" +#include class stopwatch { -private: - LARGE_INTEGER m_elapsed; - LARGE_INTEGER m_last_start_time; - LARGE_INTEGER m_last_stop_time; - LARGE_INTEGER m_frequency; + typedef decltype(std::chrono::steady_clock::now()) clock_t; + typedef decltype(std::chrono::steady_clock::now() - std::chrono::steady_clock::now()) duration_t; + + clock_t m_start; + duration_t m_elapsed; +#if Z3DEBUG + bool m_running = false; +#endif + + // FIXME: just use auto with VS 2015+ + static clock_t get() { + return std::chrono::steady_clock::now(); + } public: stopwatch() { - QueryPerformanceFrequency(&m_frequency); - reset(); + reset(); } - ~stopwatch() {}; - - void add (const stopwatch &s) {/* TODO */} - - void reset() { m_elapsed.QuadPart = 0; } - - void start() { - QueryPerformanceCounter(&m_last_start_time); - } - - void stop() { - QueryPerformanceCounter(&m_last_stop_time); - m_elapsed.QuadPart += m_last_stop_time.QuadPart - m_last_start_time.QuadPart; + void add(const stopwatch &s) { + m_elapsed += s.m_elapsed; } - double get_seconds() const { - return static_cast(m_elapsed.QuadPart / static_cast(m_frequency.QuadPart)) ; + void reset() { + m_elapsed = duration_t::zero(); + DEBUG_CODE(m_running = false;); + } + + void start() { + SASSERT(!m_running); + DEBUG_CODE(m_running = true;); + m_start = get(); + } + + void stop() { + SASSERT(m_running); + DEBUG_CODE(m_running = false;); + m_elapsed += get() - m_start; + } + + double get_seconds() const { + return std::chrono::duration_cast(m_elapsed).count() / 1000.0; } double get_current_seconds() const { - LARGE_INTEGER t; - QueryPerformanceCounter(&t); - return static_cast( (t.QuadPart - m_last_start_time.QuadPart) / static_cast(m_frequency.QuadPart)); + return std::chrono::duration_cast(get() - m_start).count() / 1000.0; } }; -#undef max -#undef min - - -#elif defined(__APPLE__) && defined (__MACH__) // macOS - -#include -#include - -class stopwatch { - unsigned long long m_time; // elapsed time in ns - bool m_running; - clock_serv_t m_host_clock; - mach_timespec_t m_start; - -public: - stopwatch():m_time(0), m_running(false) { - host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &m_host_clock); - } - - ~stopwatch() {} - - void add (const stopwatch &s) {m_time += s.m_time;} - - void reset() { - m_time = 0ull; - } - - void start() { - if (!m_running) { - clock_get_time(m_host_clock, &m_start); - m_running = true; - } - } - - void stop() { - if (m_running) { - mach_timespec_t _stop; - clock_get_time(m_host_clock, &_stop); - m_time += (_stop.tv_sec - m_start.tv_sec) * 1000000000ull; - m_time += (_stop.tv_nsec - m_start.tv_nsec); - m_running = false; - } - } - - double get_seconds() const { - if (m_running) { - const_cast(this)->stop(); - /* update m_time */ - const_cast(this)->start(); - } - return static_cast(m_time)/static_cast(1000000000ull); - } - - double get_current_seconds() const { - return get_seconds(); - } -}; - - -#else // Linux - -#include - -#ifndef CLOCK_PROCESS_CPUTIME_ID -/* BSD */ -# define CLOCK_PROCESS_CPUTIME_ID CLOCK_MONOTONIC -#endif - -class stopwatch { - unsigned long long m_time; // elapsed time in ns - bool m_running; - struct timespec m_start; - -public: - stopwatch():m_time(0), m_running(false) { - } - - ~stopwatch() {} - - void add (const stopwatch &s) {m_time += s.m_time;} - - void reset() { - m_time = 0ull; - } - - void start() { - if (!m_running) { - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &m_start); - m_running = true; - } - } - - void stop() { - if (m_running) { - struct timespec _stop; - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &_stop); - m_time += (_stop.tv_sec - m_start.tv_sec) * 1000000000ull; - if (m_time != 0 || _stop.tv_nsec >= m_start.tv_nsec) - m_time += (_stop.tv_nsec - m_start.tv_nsec); - m_running = false; - } - } - - double get_seconds() const { - if (m_running) { - const_cast(this)->stop(); - /* update m_time */ - const_cast(this)->start(); - } - return static_cast(m_time)/static_cast(1000000000ull); - } - - double get_current_seconds() const { - return get_seconds(); - } -}; - -#endif struct scoped_watch { stopwatch &m_sw; diff --git a/src/util/str_hashtable.h b/src/util/str_hashtable.h index e12cdc7a7..111d54ac8 100644 --- a/src/util/str_hashtable.h +++ b/src/util/str_hashtable.h @@ -28,7 +28,7 @@ struct str_hash_proc { unsigned operator()(char const * s) const { return string_hash(s, static_cast(strlen(s)), 17); } }; struct str_eq_proc { bool operator()(char const * s1, char const * s2) const { return strcmp(s1, s2) == 0; } }; -typedef ptr_hashtable str_hashtable; +typedef ptr_hashtable str_hashtable; #endif /* STR_HASHTABLE_H_ */ diff --git a/src/util/symbol.cpp b/src/util/symbol.cpp index 90b245e6c..3f0e7c967 100644 --- a/src/util/symbol.cpp +++ b/src/util/symbol.cpp @@ -35,20 +35,19 @@ class internal_symbol_table { public: char const * get_str(char const * d) { - char * result; + const char * result; #pragma omp critical (cr_symbol) { - char * r_d = const_cast(d); str_hashtable::entry * e; - if (m_table.insert_if_not_there_core(r_d, e)) { + if (m_table.insert_if_not_there_core(d, e)) { // new entry size_t l = strlen(d); // store the hash-code before the string size_t * mem = static_cast(m_region.allocate(l + 1 + sizeof(size_t))); *mem = e->get_hash(); mem++; - result = reinterpret_cast(mem); - memcpy(result, d, l+1); + result = reinterpret_cast(mem); + memcpy(mem, d, l+1); // update the entry with the new ptr. e->set_data(result); } diff --git a/src/util/symbol.h b/src/util/symbol.h index 40844cf3b..90a4eeb38 100644 --- a/src/util/symbol.h +++ b/src/util/symbol.h @@ -56,7 +56,7 @@ public: explicit symbol(char const * d); explicit symbol(unsigned idx): m_data(BOXTAGINT(char const *, idx, 1)) { -#ifndef _AMD64_ +#if !defined(__LP64__) && !defined(_WIN64) SASSERT(idx < (SIZE_MAX >> PTR_ALIGNMENT)); #endif } diff --git a/src/util/timeout.cpp b/src/util/timeout.cpp index ad02a747d..1a92ae867 100644 --- a/src/util/timeout.cpp +++ b/src/util/timeout.cpp @@ -19,7 +19,6 @@ Revision History: --*/ #include -#include "util/z3_omp.h" #include "util/util.h" #include "util/timeout.h" #include "util/error_codes.h" @@ -34,26 +33,21 @@ namespace { class g_timeout_eh : public event_handler { public: void operator()(event_handler_caller_t caller_id) override { - #pragma omp critical (g_timeout_cs) - { - std::cout << "timeout\n"; - m_caller_id = caller_id; - if (g_on_timeout) - g_on_timeout(); - if (g_timeout) - delete g_timeout; - g_timeout = nullptr; - throw z3_error(ERR_TIMEOUT); - } + std::cout << "timeout\n"; + m_caller_id = caller_id; + if (g_on_timeout) + g_on_timeout(); + throw z3_error(ERR_TIMEOUT); } }; } -void set_timeout(long ms) { - if (g_timeout) - delete g_timeout; +static g_timeout_eh eh; - g_timeout = new scoped_timer(ms, new g_timeout_eh()); +void set_timeout(long ms) { + SASSERT(!g_timeout); + // this is leaked, but since it's only used in the shell, it's ok + g_timeout = new scoped_timer(ms, &eh); } void register_on_timeout_proc(void (*proc)()) { diff --git a/src/util/timer.cpp b/src/util/timer.cpp deleted file mode 100644 index 2d80b732c..000000000 --- a/src/util/timer.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - timer.cpp - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2009-01-06. - -Revision History: - ---*/ -#include "util/util.h" -#include "util/memory_manager.h" -#include "util/stopwatch.h" -#include "util/timer.h" - -timer::timer(){ - m_watch = alloc(stopwatch); - start(); -} - -timer::~timer() { - dealloc(m_watch); -} - -void timer::start() { - m_watch->start(); -} - -double timer::get_seconds() { - return m_watch->get_current_seconds(); -} - diff --git a/src/util/timer.h b/src/util/timer.h index b4a69f8bf..e2ddb55e7 100644 --- a/src/util/timer.h +++ b/src/util/timer.h @@ -19,21 +19,29 @@ Revision History: #ifndef TIMER_H_ #define TIMER_H_ -class stopwatch; +#include "util/stopwatch.h" /** - \brief Wrapper for the stopwatch class. It hides windows.h dependency. + \brief Wrapper for the stopwatch class. */ class timer { - stopwatch * m_watch; + stopwatch m_watch; public: - timer(); - ~timer(); - void start(); - double get_seconds(); - bool timeout(unsigned secs) { return secs > 0 && secs != UINT_MAX && get_seconds() > secs; } - bool ms_timeout(unsigned ms) { return ms > 0 && ms != UINT_MAX && get_seconds() * 1000 > ms; } + timer() { + m_watch.start(); + } + + double get_seconds() const { + return m_watch.get_current_seconds(); + } + + bool timeout(unsigned secs) const { + return secs != 0 && secs != UINT_MAX && get_seconds() > secs; + } + + bool ms_timeout(unsigned ms) const { + return ms != 0 && ms != UINT_MAX && get_seconds() * 1000 > ms; + } }; #endif /* TIMER_H_ */ - diff --git a/src/util/trace.cpp b/src/util/trace.cpp index 68437ab92..e4b210021 100644 --- a/src/util/trace.cpp +++ b/src/util/trace.cpp @@ -38,7 +38,7 @@ void finalize_trace() { } void enable_trace(const char * tag) { - get_enabled_trace_tags().insert(const_cast(tag)); + get_enabled_trace_tags().insert(tag); } void enable_all_trace(bool flag) { @@ -46,12 +46,12 @@ void enable_all_trace(bool flag) { } void disable_trace(const char * tag) { - get_enabled_trace_tags().erase(const_cast(tag)); + get_enabled_trace_tags().erase(tag); } bool is_trace_enabled(const char * tag) { return g_enable_all_trace_tags || - (g_enabled_trace_tags && get_enabled_trace_tags().contains(const_cast(tag))); + (g_enabled_trace_tags && get_enabled_trace_tags().contains(tag)); } void close_trace() { diff --git a/src/util/trace.h b/src/util/trace.h index 1a245036f..77c66afd4 100644 --- a/src/util/trace.h +++ b/src/util/trace.h @@ -24,10 +24,6 @@ Revision History: #undef max #undef min #endif -#ifdef __APPLE__ -#undef max -#undef min -#endif #include #ifdef _TRACE diff --git a/src/util/util.h b/src/util/util.h index b9f3bdc28..0ca02bc84 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -63,14 +63,6 @@ static_assert(sizeof(int64_t) == 8, "64 bits"); #define VEC2PTR(_x_) ((_x_).size() ? &(_x_)[0] : 0) -#ifdef _WINDOWS -// Disable thread local declspec as it seems to not work downlevel. -// #define THREAD_LOCAL __declspec(thread) -#define THREAD_LOCAL -#else -#define THREAD_LOCAL -#endif - #ifdef _MSC_VER # define STD_CALL __cdecl #else diff --git a/src/util/warning.cpp b/src/util/warning.cpp index 1e9f8a484..5ffc38527 100644 --- a/src/util/warning.cpp +++ b/src/util/warning.cpp @@ -88,7 +88,7 @@ void format2ostream(std::ostream & out, char const* msg, va_list args) { #ifdef _WINDOWS size_t msg_len = _vscprintf(msg, args_copy); #else - size_t msg_len = vsnprintf(NULL, 0, msg, args_copy); + size_t msg_len = vsnprintf(nullptr, 0, msg, args_copy); #endif va_end(args_copy);